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))
99 #define CRITICAL(...) EINA_LOG_DOM_CRIT(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__)
104 #define ERR(...) EINA_LOG_DOM_ERR(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__)
109 #define WRN(...) EINA_LOG_DOM_WARN(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__)
114 #define INF(...) EINA_LOG_DOM_INFO(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__)
119 #define DBG(...) EINA_LOG_DOM_DBG(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__)
121 #define TB_NULL_CHECK(null_check, ...) \
126 ERR("%s is NULL while it shouldn't be, please notify developers.", #null_check); \
127 return __VA_ARGS__; \
132 /* private struct for textblock object internal data */
135 * @typedef Evas_Object_Textblock
136 * The actual textblock object.
138 typedef struct _Evas_Object_Textblock Evas_Object_Textblock;
141 * @typedef Evas_Object_Style_Tag
142 * The structure used for finding style tags.
144 typedef struct _Evas_Object_Style_Tag Evas_Object_Style_Tag;
147 * @typedef Evas_Object_Style_Tag
148 * The structure used for finding style tags.
150 typedef struct _Evas_Object_Style_Tag_Base Evas_Object_Style_Tag_Base;
153 * @typedef Evas_Object_Textblock_Node_Text
156 typedef struct _Evas_Object_Textblock_Node_Text Evas_Object_Textblock_Node_Text;
159 typedef struct _Evas_Object_Textblock_Node_Format Evas_Object_Textblock_Node_Format;
164 * @typedef Evas_Object_Textblock_Paragraph
165 * A layouting paragraph.
167 typedef struct _Evas_Object_Textblock_Paragraph Evas_Object_Textblock_Paragraph;
170 * @typedef Evas_Object_Textblock_Line
173 typedef struct _Evas_Object_Textblock_Line Evas_Object_Textblock_Line;
176 * @typedef Evas_Object_Textblock_Item
179 typedef struct _Evas_Object_Textblock_Item Evas_Object_Textblock_Item;
182 * @typedef Evas_Object_Textblock_Item
183 * A layouting text item.
185 typedef struct _Evas_Object_Textblock_Text_Item Evas_Object_Textblock_Text_Item;
188 * @typedef Evas_Object_Textblock_Format_Item
189 * A layouting format item.
191 typedef struct _Evas_Object_Textblock_Format_Item Evas_Object_Textblock_Format_Item;
194 * @typedef Evas_Object_Textblock_Format
195 * A textblock format.
197 typedef struct _Evas_Object_Textblock_Format Evas_Object_Textblock_Format;
201 * @def IS_AT_END(ti, ind)
202 * Return true if ind is at the end of the text item, false otherwise.
204 #define IS_AT_END(ti, ind) (ind == ti->text_props.text_len)
208 * @def MOVE_PREV_UNTIL(limit, ind)
209 * This decrements ind as long as ind > limit.
211 #define MOVE_PREV_UNTIL(limit, ind) \
214 if ((limit) < (ind)) \
221 * @def MOVE_NEXT_UNTIL(limit, ind)
222 * This increments ind as long as ind < limit
224 #define MOVE_NEXT_UNTIL(limit, ind) \
227 if ((ind) < (limit)) \
234 * @def GET_ITEM_TEXT(ti)
235 * Returns a const reference to the text of the ti (not null terminated).
237 #define GET_ITEM_TEXT(ti) \
238 (((ti)->parent.text_node) ? \
239 (eina_ustrbuf_string_get((ti)->parent.text_node->unicode) + \
240 (ti)->parent.text_pos) : EINA_UNICODE_EMPTY_STRING)
243 * @def _FORMAT_IS_CLOSER_OF(base, closer, closer_len)
244 * Returns true if closer is the closer of base.
246 #define _FORMAT_IS_CLOSER_OF(base, closer, closer_len) \
247 (!strncmp(base, closer, closer_len) && \
248 (!base[closer_len] || \
249 (base[closer_len] == '=') || \
250 _is_white(base[closer_len])))
252 /*FIXME: document the structs and struct items. */
253 struct _Evas_Object_Style_Tag_Base
261 struct _Evas_Object_Style_Tag
264 Evas_Object_Style_Tag_Base tag;
267 struct _Evas_Object_Textblock_Node_Text
270 Eina_UStrbuf *unicode;
272 Evas_Object_Textblock_Node_Format *format_node;
273 Evas_Object_Textblock_Paragraph *par;
275 Eina_Bool is_new : 1;
278 struct _Evas_Object_Textblock_Node_Format
282 const char *orig_format;
283 Evas_Object_Textblock_Node_Text *text_node;
285 unsigned char anchor : 2;
286 Eina_Bool opener : 1;
287 Eina_Bool own_closer : 1;
288 Eina_Bool visible : 1;
289 Eina_Bool format_change : 1;
290 Eina_Bool is_new : 1;
293 /* The default tags to use */
294 static const Evas_Object_Style_Tag_Base default_tags[] = {
295 { "b", "+ font_weight=Bold", 1, 18 },
296 { "i", "+ font_style=Italic", 1, 19 }};
298 #define ANCHOR_NONE 0
300 #define ANCHOR_ITEM 2
305 * A convinience macro for casting to a text node.
307 #define _NODE_TEXT(x) ((Evas_Object_Textblock_Node_Text *) (x))
310 * @def _NODE_FORMAT(x)
311 * A convinience macro for casting to a format node.
313 #define _NODE_FORMAT(x) ((Evas_Object_Textblock_Node_Format *) (x))
317 * A convinience macro for casting to a generic item.
319 #define _ITEM(x) ((Evas_Object_Textblock_Item *) (x))
323 * A convinience macro for casting to a text item.
325 #define _ITEM_TEXT(x) ((Evas_Object_Textblock_Text_Item *) (x))
328 * @def _ITEM_FORMAT(x)
329 * A convinience macro for casting to a format item.
331 #define _ITEM_FORMAT(x) ((Evas_Object_Textblock_Format_Item *) (x))
333 struct _Evas_Object_Textblock_Paragraph
336 Evas_Object_Textblock_Line *lines;
337 Evas_Object_Textblock_Node_Text *text_node;
338 Eina_List *logical_items;
339 Evas_BiDi_Paragraph_Props *bidi_props; /* Only valid during layout */
340 Evas_BiDi_Direction direction;
343 Eina_Bool is_bidi : 1;
344 Eina_Bool visible : 1;
345 Eina_Bool rendered : 1;
348 struct _Evas_Object_Textblock_Line
351 Evas_Object_Textblock_Item *items;
352 Evas_Object_Textblock_Paragraph *par;
353 Evas_Coord x, y, w, h;
358 typedef enum _Evas_Textblock_Item_Type
360 EVAS_TEXTBLOCK_ITEM_TEXT,
361 EVAS_TEXTBLOCK_ITEM_FORMAT,
362 } Evas_Textblock_Item_Type;
364 struct _Evas_Object_Textblock_Item
367 Evas_Textblock_Item_Type type;
368 Evas_Object_Textblock_Node_Text *text_node;
369 Evas_Object_Textblock_Format *format;
374 Evas_Coord adv, x, w, h;
375 Eina_Bool merge : 1; /* Indicates whether this
376 item should merge to the
377 previous item or not */
378 Eina_Bool visually_deleted : 1;
379 /* Indicates whether this
380 item is used in the visual
384 struct _Evas_Object_Textblock_Text_Item
386 Evas_Object_Textblock_Item parent;
387 Evas_Text_Props text_props;
389 Evas_Coord x_adjustment; /* Used to indicate by how
390 much we adjusted sizes */
393 struct _Evas_Object_Textblock_Format_Item
395 Evas_Object_Textblock_Item parent;
396 Evas_BiDi_Direction bidi_dir;
399 unsigned char vsize : 2;
400 unsigned char size : 2;
401 Eina_Bool formatme : 1;
404 struct _Evas_Object_Textblock_Format
406 Evas_Object_Textblock_Node_Format *fnode;
410 Evas_Font_Description *fdesc;
417 unsigned char r, g, b, a;
418 } normal, underline, underline2, underline_dash, outline, shadow, glow, glow2, backing,
428 int underline_dash_width;
429 int underline_dash_gap;
435 Eina_Bool wrap_word : 1;
436 Eina_Bool wrap_char : 1;
437 Eina_Bool wrap_mixed : 1;
438 Eina_Bool underline : 1;
439 Eina_Bool underline2 : 1;
440 Eina_Bool underline_dash : 1;
441 Eina_Bool strikethrough : 1;
442 Eina_Bool backing : 1;
443 Eina_Bool password : 1;
444 Eina_Bool halign_auto : 1;
447 struct _Evas_Textblock_Style
449 const char *style_text;
451 Evas_Object_Style_Tag *tags;
453 Eina_Bool delete_me : 1;
456 struct _Evas_Textblock_Cursor
460 Evas_Object_Textblock_Node_Text *node;
463 /* Size of the index array */
464 #define TEXTBLOCK_PAR_INDEX_SIZE 10
465 struct _Evas_Object_Textblock
468 Evas_Textblock_Style *style;
469 Evas_Textblock_Style *style_user;
470 Evas_Textblock_Cursor *cursor;
472 Evas_Object_Textblock_Node_Text *text_nodes;
473 Evas_Object_Textblock_Node_Format *format_nodes;
476 Evas_Object_Textblock_Paragraph *paragraphs;
477 Evas_Object_Textblock_Paragraph *par_index[TEXTBLOCK_PAR_INDEX_SIZE];
479 Evas_Object_Textblock_Text_Item *ellip_ti;
480 Eina_List *anchors_a;
481 Eina_List *anchors_item;
490 const char *bidi_delimiters;
495 Eina_Bool redraw : 1;
496 Eina_Bool changed : 1;
497 Eina_Bool content_changed : 1;
498 Eina_Bool format_changed : 1;
499 Eina_Bool have_ellipsis : 1;
500 Eina_Bool legacy_newline : 1;
503 /* private methods for textblock objects */
504 static void evas_object_textblock_init(Evas_Object *obj);
505 static void *evas_object_textblock_new(void);
506 static void evas_object_textblock_render(Evas_Object *obj, void *output, void *context, void *surface, int x, int y);
507 static void evas_object_textblock_free(Evas_Object *obj);
508 static void evas_object_textblock_render_pre(Evas_Object *obj);
509 static void evas_object_textblock_render_post(Evas_Object *obj);
511 static unsigned int evas_object_textblock_id_get(Evas_Object *obj);
512 static unsigned int evas_object_textblock_visual_id_get(Evas_Object *obj);
513 static void *evas_object_textblock_engine_data_get(Evas_Object *obj);
515 static int evas_object_textblock_is_opaque(Evas_Object *obj);
516 static int evas_object_textblock_was_opaque(Evas_Object *obj);
518 static void evas_object_textblock_coords_recalc(Evas_Object *obj);
520 static void evas_object_textblock_scale_update(Evas_Object *obj);
522 static const Evas_Object_Func object_func =
524 /* methods (compulsory) */
525 evas_object_textblock_free,
526 evas_object_textblock_render,
527 evas_object_textblock_render_pre,
528 evas_object_textblock_render_post,
529 evas_object_textblock_id_get,
530 evas_object_textblock_visual_id_get,
531 evas_object_textblock_engine_data_get,
532 /* these are optional. NULL = nothing */
537 evas_object_textblock_is_opaque,
538 evas_object_textblock_was_opaque,
541 evas_object_textblock_coords_recalc,
542 evas_object_textblock_scale_update,
548 /* the actual api call to add a textblock */
551 Evas_Object_Textblock *o; \
552 MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ); \
555 o = (Evas_Object_Textblock *)(obj->object_data); \
556 MAGIC_CHECK(o, Evas_Object_Textblock, MAGIC_OBJ_TEXTBLOCK); \
560 #define TB_HEAD_RETURN(x) \
561 Evas_Object_Textblock *o; \
562 MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ); \
565 o = (Evas_Object_Textblock *)(obj->object_data); \
566 MAGIC_CHECK(o, Evas_Object_Textblock, MAGIC_OBJ_TEXTBLOCK); \
572 static Eina_Bool _evas_textblock_cursor_is_at_the_end(const Evas_Textblock_Cursor *cur);
573 static void _evas_textblock_node_text_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *n);
574 static Evas_Object_Textblock_Node_Format *_evas_textblock_cursor_node_format_before_or_at_pos_get(const Evas_Textblock_Cursor *cur);
575 static size_t _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt);
576 static void _evas_textblock_node_format_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n, int visual_adjustment);
577 static void _evas_textblock_node_format_free(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n);
578 static void _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n);
579 static void _evas_textblock_changed(Evas_Object_Textblock *o, Evas_Object *obj);
580 static void _evas_textblock_invalidate_all(Evas_Object_Textblock *o);
581 static void _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Text *n, size_t start, int offset);
582 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);
587 * Clears the textblock style passed except for the style_text which is replaced.
588 * @param ts The ts to be cleared. Must not be NULL.
589 * @param style_text the style's text.
592 _style_replace(Evas_Textblock_Style *ts, const char *style_text)
594 eina_stringshare_replace(&ts->style_text, style_text);
595 if (ts->default_tag) free(ts->default_tag);
598 Evas_Object_Style_Tag *tag;
600 tag = (Evas_Object_Style_Tag *)ts->tags;
601 ts->tags = (Evas_Object_Style_Tag *)eina_inlist_remove(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
603 free(tag->tag.replace);
606 ts->default_tag = NULL;
612 * Clears the textblock style passed.
613 * @param ts The ts to be cleared. Must not be NULL.
616 _style_clear(Evas_Textblock_Style *ts)
618 _style_replace(ts, NULL);
623 * Searches inside the tags stored in the style for the tag matching s.
624 * @param ts The ts to be cleared. Must not be NULL.
625 * @param s The tag to be matched.
626 * @param tag_len the length of the tag string.
627 * @param[out] replace_len The length of the replcaement found. - Must not be NULL.
628 * @return The replacement string found.
630 static inline const char *
631 _style_match_tag(const Evas_Textblock_Style *ts, const char *s, size_t tag_len, size_t *replace_len)
633 Evas_Object_Style_Tag *tag;
635 /* Try the style tags */
636 EINA_INLIST_FOREACH(ts->tags, tag)
638 if (tag->tag.tag_len != tag_len) continue;
639 if (!strncmp(tag->tag.tag, s, tag_len))
641 *replace_len = tag->tag.replace_len;
642 return tag->tag.replace;
646 /* Try the default tags */
649 const Evas_Object_Style_Tag_Base *btag;
650 for (btag = default_tags, i = 0 ;
651 i < (sizeof(default_tags) / sizeof(default_tags[0])) ;
654 if (btag->tag_len != tag_len) continue;
655 if (!strncmp(btag->tag, s, tag_len))
657 *replace_len = btag->replace_len;
658 return btag->replace;
669 * Clears all the nodes (text and format) of the textblock object.
670 * @param obj The evas object, must not be NULL.
673 _nodes_clear(const Evas_Object *obj)
675 Evas_Object_Textblock *o;
677 o = (Evas_Object_Textblock *)(obj->object_data);
678 while (o->text_nodes)
680 Evas_Object_Textblock_Node_Text *n;
683 o->text_nodes = _NODE_TEXT(eina_inlist_remove(
684 EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n)));
685 _evas_textblock_node_text_free(n);
687 while (o->format_nodes)
689 Evas_Object_Textblock_Node_Format *n;
692 o->format_nodes = _NODE_FORMAT(eina_inlist_remove(EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n)));
693 _evas_textblock_node_format_free(o, n);
699 * Unrefs and frees (if needed) a textblock format.
700 * @param obj The Evas_Object, Must not be NULL.
701 * @param fmt the format to be cleaned, must not be NULL.
704 _format_unref_free(const Evas_Object *obj, Evas_Object_Textblock_Format *fmt)
707 if (fmt->ref > 0) return;
708 if (fmt->font.fdesc) evas_font_desc_unref(fmt->font.fdesc);
709 if (fmt->font.source) eina_stringshare_del(fmt->font.source);
710 evas_font_free(obj->layer->evas, fmt->font.font);
717 * @param obj The evas object, must not be NULL.
718 * @param ln the layout line on which the item is in, must not be NULL.
719 * @param it the layout item to be freed
722 _item_free(const Evas_Object *obj, Evas_Object_Textblock_Line *ln, Evas_Object_Textblock_Item *it)
724 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
726 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
728 evas_common_text_props_content_unref(&ti->text_props);
732 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
734 if (fi->item) eina_stringshare_del(fi->item);
736 _format_unref_free(obj, it->format);
739 ln->items = (Evas_Object_Textblock_Item *) eina_inlist_remove(
740 EINA_INLIST_GET(ln->items), EINA_INLIST_GET(ln->items));
747 * Free a layout line.
748 * @param obj The evas object, must not be NULL.
749 * @param ln the layout line to be freed, must not be NULL.
752 _line_free(Evas_Object_Textblock_Line *ln)
754 /* Items are freed from the logical list, except for the ellip item */
758 /* table of html escapes (that i can find) this should be ordered with the
759 * most common first as it's a linear search to match - no hash for this.
761 * these are stored as one large string and one additional array that
762 * contains the offsets to the tokens for space efficiency.
766 * @var escape_strings[]
767 * This string consists of NULL terminated pairs of strings, the first of
768 * every pair is an escape and the second is the value of the escape.
770 static const char escape_strings[] =
771 /* most common escaped stuff */
777 " \0" "\xc2\xa0\0"
778 "¡\0" "\xc2\xa1\0"
779 "¢\0" "\xc2\xa2\0"
780 "£\0" "\xc2\xa3\0"
781 "¤\0" "\xc2\xa4\0"
782 "¥\0" "\xc2\xa5\0"
783 "¦\0" "\xc2\xa6\0"
784 "§\0" "\xc2\xa7\0"
785 "¨\0" "\xc2\xa8\0"
786 "©\0" "\xc2\xa9\0"
787 "ª\0" "\xc2\xaa\0"
788 "«\0" "\xc2\xab\0"
789 "¬\0" "\xc2\xac\0"
790 "®\0" "\xc2\xae\0"
791 "¯\0" "\xc2\xaf\0"
792 "°\0" "\xc2\xb0\0"
793 "±\0" "\xc2\xb1\0"
794 "²\0" "\xc2\xb2\0"
795 "³\0" "\xc2\xb3\0"
796 "´\0" "\xc2\xb4\0"
797 "µ\0" "\xc2\xb5\0"
798 "¶\0" "\xc2\xb6\0"
799 "·\0" "\xc2\xb7\0"
800 "¸\0" "\xc2\xb8\0"
801 "¹\0" "\xc2\xb9\0"
802 "º\0" "\xc2\xba\0"
803 "»\0" "\xc2\xbb\0"
804 "¼\0" "\xc2\xbc\0"
805 "½\0" "\xc2\xbd\0"
806 "¾\0" "\xc2\xbe\0"
807 "¿\0" "\xc2\xbf\0"
808 "À\0" "\xc3\x80\0"
809 "Á\0" "\xc3\x81\0"
810 "Â\0" "\xc3\x82\0"
811 "Ã\0" "\xc3\x83\0"
812 "Ä\0" "\xc3\x84\0"
813 "Å\0" "\xc3\x85\0"
814 "&Aelig;\0" "\xc3\x86\0"
815 "Ç\0" "\xc3\x87\0"
816 "È\0" "\xc3\x88\0"
817 "É\0" "\xc3\x89\0"
818 "Ê\0" "\xc3\x8a\0"
819 "Ë\0" "\xc3\x8b\0"
820 "Ì\0" "\xc3\x8c\0"
821 "Í\0" "\xc3\x8d\0"
822 "Î\0" "\xc3\x8e\0"
823 "Ï\0" "\xc3\x8f\0"
824 "&Eth;\0" "\xc3\x90\0"
825 "Ñ\0" "\xc3\x91\0"
826 "Ò\0" "\xc3\x92\0"
827 "Ó\0" "\xc3\x93\0"
828 "Ô\0" "\xc3\x94\0"
829 "Õ\0" "\xc3\x95\0"
830 "Ö\0" "\xc3\x96\0"
831 "×\0" "\xc3\x97\0"
832 "Ø\0" "\xc3\x98\0"
833 "Ù\0" "\xc3\x99\0"
834 "Ú\0" "\xc3\x9a\0"
835 "Û\0" "\xc3\x9b\0"
836 "Ý\0" "\xc3\x9d\0"
837 "&Thorn;\0" "\xc3\x9e\0"
838 "ß\0" "\xc3\x9f\0"
839 "à\0" "\xc3\xa0\0"
840 "á\0" "\xc3\xa1\0"
841 "â\0" "\xc3\xa2\0"
842 "ã\0" "\xc3\xa3\0"
843 "ä\0" "\xc3\xa4\0"
844 "å\0" "\xc3\xa5\0"
845 "æ\0" "\xc3\xa6\0"
846 "ç\0" "\xc3\xa7\0"
847 "è\0" "\xc3\xa8\0"
848 "é\0" "\xc3\xa9\0"
849 "ê\0" "\xc3\xaa\0"
850 "ë\0" "\xc3\xab\0"
851 "ì\0" "\xc3\xac\0"
852 "í\0" "\xc3\xad\0"
853 "î\0" "\xc3\xae\0"
854 "ï\0" "\xc3\xaf\0"
855 "ð\0" "\xc3\xb0\0"
856 "ñ\0" "\xc3\xb1\0"
857 "ò\0" "\xc3\xb2\0"
858 "ó\0" "\xc3\xb3\0"
859 "ô\0" "\xc3\xb4\0"
860 "õ\0" "\xc3\xb5\0"
861 "ö\0" "\xc3\xb6\0"
862 "÷\0" "\xc3\xb7\0"
863 "ø\0" "\xc3\xb8\0"
864 "ù\0" "\xc3\xb9\0"
865 "ú\0" "\xc3\xba\0"
866 "û\0" "\xc3\xbb\0"
867 "ü\0" "\xc3\xbc\0"
868 "ý\0" "\xc3\xbd\0"
869 "þ\0" "\xc3\xbe\0"
870 "ÿ\0" "\xc3\xbf\0"
871 "α\0" "\xce\x91\0"
872 "β\0" "\xce\x92\0"
873 "γ\0" "\xce\x93\0"
874 "δ\0" "\xce\x94\0"
875 "ε\0" "\xce\x95\0"
876 "ζ\0" "\xce\x96\0"
877 "η\0" "\xce\x97\0"
878 "θ\0" "\xce\x98\0"
879 "ι\0" "\xce\x99\0"
880 "κ\0" "\xce\x9a\0"
881 "λ\0" "\xce\x9b\0"
882 "μ\0" "\xce\x9c\0"
883 "ν\0" "\xce\x9d\0"
884 "ξ\0" "\xce\x9e\0"
885 "ο\0" "\xce\x9f\0"
886 "π\0" "\xce\xa0\0"
887 "ρ\0" "\xce\xa1\0"
888 "σ\0" "\xce\xa3\0"
889 "τ\0" "\xce\xa4\0"
890 "υ\0" "\xce\xa5\0"
891 "φ\0" "\xce\xa6\0"
892 "χ\0" "\xce\xa7\0"
893 "ψ\0" "\xce\xa8\0"
894 "ω\0" "\xce\xa9\0"
895 "…\0" "\xe2\x80\xa6\0"
896 "€\0" "\xe2\x82\xac\0"
897 "←\0" "\xe2\x86\x90\0"
898 "↑\0" "\xe2\x86\x91\0"
899 "→\0" "\xe2\x86\x92\0"
900 "↓\0" "\xe2\x86\x93\0"
901 "↔\0" "\xe2\x86\x94\0"
902 "←\0" "\xe2\x87\x90\0"
903 "→\0" "\xe2\x87\x92\0"
904 "∀\0" "\xe2\x88\x80\0"
905 "∃\0" "\xe2\x88\x83\0"
906 "∇\0" "\xe2\x88\x87\0"
907 "∏\0" "\xe2\x88\x8f\0"
908 "∑\0" "\xe2\x88\x91\0"
909 "∧\0" "\xe2\x88\xa7\0"
910 "∨\0" "\xe2\x88\xa8\0"
911 "∫\0" "\xe2\x88\xab\0"
912 "≠\0" "\xe2\x89\xa0\0"
913 "≡\0" "\xe2\x89\xa1\0"
914 "⊕\0" "\xe2\x8a\x95\0"
915 "⊥\0" "\xe2\x8a\xa5\0"
916 "†\0" "\xe2\x80\xa0\0"
917 "‡\0" "\xe2\x80\xa1\0"
918 "•\0" "\xe2\x80\xa2\0"
921 EVAS_MEMPOOL(_mp_obj);
925 * Checks if a char is a whitespace.
926 * @param c the unicode codepoint.
927 * @return @c EINA_TRUE if the unicode codepoint is a whitespace, @c EINA_FALSE
931 _is_white(Eina_Unicode c)
934 * unicode list of whitespace chars
936 * 0009..000D <control-0009>..<control-000D>
938 * 0085 <control-0085>
939 * 00A0 NO-BREAK SPACE
940 * 1680 OGHAM SPACE MARK
941 * 180E MONGOLIAN VOWEL SEPARATOR
942 * 2000..200A EN QUAD..HAIR SPACE
943 * 2028 LINE SEPARATOR
944 * 2029 PARAGRAPH SEPARATOR
945 * 202F NARROW NO-BREAK SPACE
946 * 205F MEDIUM MATHEMATICAL SPACE
947 * 3000 IDEOGRAPHIC SPACE
951 ((c >= 0x9) && (c <= 0xd)) ||
956 ((c >= 0x2000) && (c <= 0x200a)) ||
969 * Prepends the text between s and p to the main cursor of the object.
971 * @param cur the cursor to prepend to.
972 * @param[in] s start of the string
973 * @param[in] p end of the string
976 _prepend_text_run(Evas_Textblock_Cursor *cur, const char *s, const char *p)
982 ts = alloca(p - s + 1);
983 strncpy(ts, s, p - s);
985 evas_textblock_cursor_text_prepend(cur, ts);
992 * Returns the numeric value of HEX chars for example for ch = 'A'
993 * the function will return 10.
995 * @param ch The HEX char.
996 * @return numeric value of HEX.
999 _hex_string_get(char ch)
1001 if ((ch >= '0') && (ch <= '9')) return (ch - '0');
1002 else if ((ch >= 'A') && (ch <= 'F')) return (ch - 'A' + 10);
1003 else if ((ch >= 'a') && (ch <= 'f')) return (ch - 'a' + 10);
1009 * Parses a string of one of the formas:
1014 * To the rgba values.
1016 * @param[in] str The string to parse - NOT NULL.
1017 * @param[out] r The Red value - NOT NULL.
1018 * @param[out] g The Green value - NOT NULL.
1019 * @param[out] b The Blue value - NOT NULL.
1020 * @param[out] a The Alpha value - NOT NULL.
1023 _format_color_parse(const char *str, unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a)
1028 *r = *g = *b = *a = 0;
1030 if (slen == 7) /* #RRGGBB */
1032 *r = (_hex_string_get(str[1]) << 4) | (_hex_string_get(str[2]));
1033 *g = (_hex_string_get(str[3]) << 4) | (_hex_string_get(str[4]));
1034 *b = (_hex_string_get(str[5]) << 4) | (_hex_string_get(str[6]));
1037 else if (slen == 9) /* #RRGGBBAA */
1039 *r = (_hex_string_get(str[1]) << 4) | (_hex_string_get(str[2]));
1040 *g = (_hex_string_get(str[3]) << 4) | (_hex_string_get(str[4]));
1041 *b = (_hex_string_get(str[5]) << 4) | (_hex_string_get(str[6]));
1042 *a = (_hex_string_get(str[7]) << 4) | (_hex_string_get(str[8]));
1044 else if (slen == 4) /* #RGB */
1046 *r = _hex_string_get(str[1]);
1047 *r = (*r << 4) | *r;
1048 *g = _hex_string_get(str[2]);
1049 *g = (*g << 4) | *g;
1050 *b = _hex_string_get(str[3]);
1051 *b = (*b << 4) | *b;
1054 else if (slen == 5) /* #RGBA */
1056 *r = _hex_string_get(str[1]);
1057 *r = (*r << 4) | *r;
1058 *g = _hex_string_get(str[2]);
1059 *g = (*g << 4) | *g;
1060 *b = _hex_string_get(str[3]);
1061 *b = (*b << 4) | *b;
1062 *a = _hex_string_get(str[4]);
1063 *a = (*a << 4) | *a;
1065 *r = (*r * *a) / 255;
1066 *g = (*g * *a) / 255;
1067 *b = (*b * *a) / 255;
1070 /* The refcount for the formats. */
1071 static int format_refcount = 0;
1072 /* Holders for the stringshares */
1073 static const char *fontstr = NULL;
1074 static const char *font_fallbacksstr = NULL;
1075 static const char *font_sizestr = NULL;
1076 static const char *font_sourcestr = NULL;
1077 static const char *font_weightstr = NULL;
1078 static const char *font_stylestr = NULL;
1079 static const char *font_widthstr = NULL;
1080 static const char *langstr = NULL;
1081 static const char *colorstr = NULL;
1082 static const char *underline_colorstr = NULL;
1083 static const char *underline2_colorstr = NULL;
1084 static const char *underline_dash_colorstr = NULL;
1085 static const char *outline_colorstr = NULL;
1086 static const char *shadow_colorstr = NULL;
1087 static const char *glow_colorstr = NULL;
1088 static const char *glow2_colorstr = NULL;
1089 static const char *backing_colorstr = NULL;
1090 static const char *strikethrough_colorstr = NULL;
1091 static const char *alignstr = NULL;
1092 static const char *valignstr = NULL;
1093 static const char *wrapstr = NULL;
1094 static const char *left_marginstr = NULL;
1095 static const char *right_marginstr = NULL;
1096 static const char *underlinestr = NULL;
1097 static const char *strikethroughstr = NULL;
1098 static const char *backingstr = NULL;
1099 static const char *stylestr = NULL;
1100 static const char *tabstopsstr = NULL;
1101 static const char *linesizestr = NULL;
1102 static const char *linerelsizestr = NULL;
1103 static const char *linegapstr = NULL;
1104 static const char *linerelgapstr = NULL;
1105 static const char *itemstr = NULL;
1106 static const char *linefillstr = NULL;
1107 static const char *ellipsisstr = NULL;
1108 static const char *passwordstr = NULL;
1109 static const char *underline_dash_widthstr = NULL;
1110 static const char *underline_dash_gapstr = NULL;
1114 * Init the format strings.
1117 _format_command_init(void)
1119 if (format_refcount == 0)
1121 fontstr = eina_stringshare_add("font");
1122 font_fallbacksstr = eina_stringshare_add("font_fallbacks");
1123 font_sizestr = eina_stringshare_add("font_size");
1124 font_sourcestr = eina_stringshare_add("font_source");
1125 font_weightstr = eina_stringshare_add("font_weight");
1126 font_stylestr = eina_stringshare_add("font_style");
1127 font_widthstr = eina_stringshare_add("font_width");
1128 langstr = eina_stringshare_add("lang");
1129 colorstr = eina_stringshare_add("color");
1130 underline_colorstr = eina_stringshare_add("underline_color");
1131 underline2_colorstr = eina_stringshare_add("underline2_color");
1132 underline_dash_colorstr = eina_stringshare_add("underline_dash_color");
1133 outline_colorstr = eina_stringshare_add("outline_color");
1134 shadow_colorstr = eina_stringshare_add("shadow_color");
1135 glow_colorstr = eina_stringshare_add("glow_color");
1136 glow2_colorstr = eina_stringshare_add("glow2_color");
1137 backing_colorstr = eina_stringshare_add("backing_color");
1138 strikethrough_colorstr = eina_stringshare_add("strikethrough_color");
1139 alignstr = eina_stringshare_add("align");
1140 valignstr = eina_stringshare_add("valign");
1141 wrapstr = eina_stringshare_add("wrap");
1142 left_marginstr = eina_stringshare_add("left_margin");
1143 right_marginstr = eina_stringshare_add("right_margin");
1144 underlinestr = eina_stringshare_add("underline");
1145 strikethroughstr = eina_stringshare_add("strikethrough");
1146 backingstr = eina_stringshare_add("backing");
1147 stylestr = eina_stringshare_add("style");
1148 tabstopsstr = eina_stringshare_add("tabstops");
1149 linesizestr = eina_stringshare_add("linesize");
1150 linerelsizestr = eina_stringshare_add("linerelsize");
1151 linegapstr = eina_stringshare_add("linegap");
1152 linerelgapstr = eina_stringshare_add("linerelgap");
1153 itemstr = eina_stringshare_add("item");
1154 linefillstr = eina_stringshare_add("linefill");
1155 ellipsisstr = eina_stringshare_add("ellipsis");
1156 passwordstr = eina_stringshare_add("password");
1157 underline_dash_widthstr = eina_stringshare_add("underline_dash_width");
1158 underline_dash_gapstr = eina_stringshare_add("underline_dash_gap");
1165 * Shutdown the format strings.
1168 _format_command_shutdown(void)
1170 if (--format_refcount > 0) return;
1172 eina_stringshare_del(fontstr);
1173 eina_stringshare_del(font_fallbacksstr);
1174 eina_stringshare_del(font_sizestr);
1175 eina_stringshare_del(font_sourcestr);
1176 eina_stringshare_del(font_weightstr);
1177 eina_stringshare_del(font_stylestr);
1178 eina_stringshare_del(font_widthstr);
1179 eina_stringshare_del(langstr);
1180 eina_stringshare_del(colorstr);
1181 eina_stringshare_del(underline_colorstr);
1182 eina_stringshare_del(underline2_colorstr);
1183 eina_stringshare_del(underline_dash_colorstr);
1184 eina_stringshare_del(outline_colorstr);
1185 eina_stringshare_del(shadow_colorstr);
1186 eina_stringshare_del(glow_colorstr);
1187 eina_stringshare_del(glow2_colorstr);
1188 eina_stringshare_del(backing_colorstr);
1189 eina_stringshare_del(strikethrough_colorstr);
1190 eina_stringshare_del(alignstr);
1191 eina_stringshare_del(valignstr);
1192 eina_stringshare_del(wrapstr);
1193 eina_stringshare_del(left_marginstr);
1194 eina_stringshare_del(right_marginstr);
1195 eina_stringshare_del(underlinestr);
1196 eina_stringshare_del(strikethroughstr);
1197 eina_stringshare_del(backingstr);
1198 eina_stringshare_del(stylestr);
1199 eina_stringshare_del(tabstopsstr);
1200 eina_stringshare_del(linesizestr);
1201 eina_stringshare_del(linerelsizestr);
1202 eina_stringshare_del(linegapstr);
1203 eina_stringshare_del(linerelgapstr);
1204 eina_stringshare_del(itemstr);
1205 eina_stringshare_del(linefillstr);
1206 eina_stringshare_del(ellipsisstr);
1207 eina_stringshare_del(passwordstr);
1208 eina_stringshare_del(underline_dash_widthstr);
1209 eina_stringshare_del(underline_dash_gapstr);
1214 * Copies str to dst while removing the \\ char, i.e unescape the escape sequences.
1216 * @param[out] dst the destination string - Should not be NULL.
1217 * @param[in] src the source string - Should not be NULL.
1220 _format_clean_param(char *dst, const char *src)
1226 for (ss = src; *ss; ss++, ds++)
1228 if ((*ss == '\\') && *(ss + 1)) ss++;
1236 * Parses the cmd and parameter and adds the parsed format to fmt.
1238 * @param obj the evas object - should not be NULL.
1239 * @param fmt The format to populate - should not be NULL.
1240 * @param[in] cmd the command to process, should be stringshared.
1241 * @param[in] param the parameter of the command.
1244 _format_command(Evas_Object *obj, Evas_Object_Textblock_Format *fmt, const char *cmd, const char *param)
1249 len = strlen(param);
1250 tmp_param = alloca(len + 1);
1252 _format_clean_param(tmp_param, param);
1254 /* If we are changing the font, create the fdesc. */
1255 if ((cmd == font_weightstr) || (cmd == font_widthstr) ||
1256 (cmd == font_stylestr) || (cmd == langstr) ||
1257 (cmd == fontstr) || (cmd == font_fallbacksstr))
1259 if (!fmt->font.fdesc)
1261 fmt->font.fdesc = evas_font_desc_new();
1263 else if (!fmt->font.fdesc->is_new)
1265 Evas_Font_Description *old = fmt->font.fdesc;
1266 fmt->font.fdesc = evas_font_desc_dup(fmt->font.fdesc);
1267 if (old) evas_font_desc_unref(old);
1274 evas_font_name_parse(fmt->font.fdesc, tmp_param);
1276 else if (cmd == font_fallbacksstr)
1278 eina_stringshare_replace(&(fmt->font.fdesc->fallbacks), tmp_param);
1280 else if (cmd == font_sizestr)
1284 v = atoi(tmp_param);
1285 if (v != fmt->font.size)
1290 else if (cmd == font_sourcestr)
1292 if ((!fmt->font.source) ||
1293 ((fmt->font.source) && (strcmp(fmt->font.source, tmp_param))))
1295 if (fmt->font.source) eina_stringshare_del(fmt->font.source);
1296 fmt->font.source = eina_stringshare_add(tmp_param);
1299 else if (cmd == font_weightstr)
1301 fmt->font.fdesc->weight = evas_font_style_find(tmp_param,
1302 tmp_param + strlen(tmp_param), EVAS_FONT_STYLE_WEIGHT);
1304 else if (cmd == font_stylestr)
1306 fmt->font.fdesc->slant = evas_font_style_find(tmp_param,
1307 tmp_param + strlen(tmp_param), EVAS_FONT_STYLE_SLANT);
1309 else if (cmd == font_widthstr)
1311 fmt->font.fdesc->width = evas_font_style_find(tmp_param,
1312 tmp_param + strlen(tmp_param), EVAS_FONT_STYLE_WIDTH);
1314 else if (cmd == langstr)
1316 eina_stringshare_replace(&(fmt->font.fdesc->lang), tmp_param);
1318 else if (cmd == colorstr)
1319 _format_color_parse(tmp_param,
1320 &(fmt->color.normal.r), &(fmt->color.normal.g),
1321 &(fmt->color.normal.b), &(fmt->color.normal.a));
1322 else if (cmd == underline_colorstr)
1323 _format_color_parse(tmp_param,
1324 &(fmt->color.underline.r), &(fmt->color.underline.g),
1325 &(fmt->color.underline.b), &(fmt->color.underline.a));
1326 else if (cmd == underline2_colorstr)
1327 _format_color_parse(tmp_param,
1328 &(fmt->color.underline2.r), &(fmt->color.underline2.g),
1329 &(fmt->color.underline2.b), &(fmt->color.underline2.a));
1330 else if (cmd == underline_dash_colorstr)
1331 _format_color_parse(tmp_param,
1332 &(fmt->color.underline_dash.r), &(fmt->color.underline_dash.g),
1333 &(fmt->color.underline_dash.b), &(fmt->color.underline_dash.a));
1334 else if (cmd == outline_colorstr)
1335 _format_color_parse(tmp_param,
1336 &(fmt->color.outline.r), &(fmt->color.outline.g),
1337 &(fmt->color.outline.b), &(fmt->color.outline.a));
1338 else if (cmd == shadow_colorstr)
1339 _format_color_parse(tmp_param,
1340 &(fmt->color.shadow.r), &(fmt->color.shadow.g),
1341 &(fmt->color.shadow.b), &(fmt->color.shadow.a));
1342 else if (cmd == glow_colorstr)
1343 _format_color_parse(tmp_param,
1344 &(fmt->color.glow.r), &(fmt->color.glow.g),
1345 &(fmt->color.glow.b), &(fmt->color.glow.a));
1346 else if (cmd == glow2_colorstr)
1347 _format_color_parse(tmp_param,
1348 &(fmt->color.glow2.r), &(fmt->color.glow2.g),
1349 &(fmt->color.glow2.b), &(fmt->color.glow2.a));
1350 else if (cmd == backing_colorstr)
1351 _format_color_parse(tmp_param,
1352 &(fmt->color.backing.r), &(fmt->color.backing.g),
1353 &(fmt->color.backing.b), &(fmt->color.backing.a));
1354 else if (cmd == strikethrough_colorstr)
1355 _format_color_parse(tmp_param,
1356 &(fmt->color.strikethrough.r), &(fmt->color.strikethrough.g),
1357 &(fmt->color.strikethrough.b), &(fmt->color.strikethrough.a));
1358 else if (cmd == alignstr)
1360 if (!strcmp(tmp_param, "auto"))
1362 fmt->halign_auto = EINA_TRUE;
1366 if (!strcmp(tmp_param, "middle")) fmt->halign = 0.5;
1367 else if (!strcmp(tmp_param, "center")) fmt->halign = 0.5;
1368 else if (!strcmp(tmp_param, "left")) fmt->halign = 0.0;
1369 else if (!strcmp(tmp_param, "right")) fmt->halign = 1.0;
1372 char *endptr = NULL;
1373 double val = strtod(tmp_param, &endptr);
1376 while (*endptr && _is_white(*endptr))
1382 if (fmt->halign < 0.0) fmt->halign = 0.0;
1383 else if (fmt->halign > 1.0) fmt->halign = 1.0;
1385 fmt->halign_auto = EINA_FALSE;
1388 else if (cmd == valignstr)
1390 if (!strcmp(tmp_param, "top")) fmt->valign = 0.0;
1391 else if (!strcmp(tmp_param, "middle")) fmt->valign = 0.5;
1392 else if (!strcmp(tmp_param, "center")) fmt->valign = 0.5;
1393 else if (!strcmp(tmp_param, "bottom")) fmt->valign = 1.0;
1394 else if (!strcmp(tmp_param, "baseline")) fmt->valign = -1.0;
1395 else if (!strcmp(tmp_param, "base")) fmt->valign = -1.0;
1398 char *endptr = NULL;
1399 double val = strtod(tmp_param, &endptr);
1402 while (*endptr && _is_white(*endptr))
1408 if (fmt->valign < 0.0) fmt->valign = 0.0;
1409 else if (fmt->valign > 1.0) fmt->valign = 1.0;
1412 else if (cmd == wrapstr)
1414 if (!strcmp(tmp_param, "word"))
1417 fmt->wrap_char = fmt->wrap_mixed = 0;
1419 else if (!strcmp(tmp_param, "char"))
1421 fmt->wrap_word = fmt->wrap_mixed = 0;
1424 else if (!strcmp(tmp_param, "mixed"))
1426 fmt->wrap_word = fmt->wrap_char = 0;
1427 fmt->wrap_mixed = 1;
1431 fmt->wrap_word = fmt->wrap_mixed = fmt->wrap_char = 0;
1434 else if (cmd == left_marginstr)
1436 if (!strcmp(tmp_param, "reset"))
1440 if (tmp_param[0] == '+')
1441 fmt->margin.l += atoi(&(tmp_param[1]));
1442 else if (tmp_param[0] == '-')
1443 fmt->margin.l -= atoi(&(tmp_param[1]));
1445 fmt->margin.l = atoi(tmp_param);
1446 if (fmt->margin.l < 0) fmt->margin.l = 0;
1449 else if (cmd == right_marginstr)
1451 if (!strcmp(tmp_param, "reset"))
1455 if (tmp_param[0] == '+')
1456 fmt->margin.r += atoi(&(tmp_param[1]));
1457 else if (tmp_param[0] == '-')
1458 fmt->margin.r -= atoi(&(tmp_param[1]));
1460 fmt->margin.r = atoi(tmp_param);
1461 if (fmt->margin.r < 0) fmt->margin.r = 0;
1464 else if (cmd == underlinestr)
1466 if (!strcmp(tmp_param, "off"))
1469 fmt->underline2 = 0;
1471 else if ((!strcmp(tmp_param, "on")) ||
1472 (!strcmp(tmp_param, "single")))
1475 fmt->underline2 = 0;
1477 else if (!strcmp(tmp_param, "double"))
1480 fmt->underline2 = 1;
1482 else if (!strcmp(tmp_param, "dashed"))
1483 fmt->underline_dash = 1;
1485 else if (cmd == strikethroughstr)
1487 if (!strcmp(tmp_param, "off"))
1488 fmt->strikethrough = 0;
1489 else if (!strcmp(tmp_param, "on"))
1490 fmt->strikethrough = 1;
1492 else if (cmd == backingstr)
1494 if (!strcmp(tmp_param, "off"))
1496 else if (!strcmp(tmp_param, "on"))
1499 else if (cmd == stylestr)
1501 char *p1, *p2, *p, *pp;
1503 p1 = alloca(len + 1);
1505 p2 = alloca(len + 1);
1508 if (!strstr(tmp_param, ",")) p1 = tmp_param;
1511 /* split string "str1,str2" into p1 and p2 (if we have more than
1512 * 1 str2 eg "str1,str2,str3,str4" then we don't care. p2 just
1513 * ends up being the last one as right now it's only valid to have
1514 * 1 comma and 2 strings */
1516 for (p = tmp_param; *p; p++)
1529 if (!strcmp(p1, "off")) fmt->style = EVAS_TEXT_STYLE_PLAIN;
1530 else if (!strcmp(p1, "none")) fmt->style = EVAS_TEXT_STYLE_PLAIN;
1531 else if (!strcmp(p1, "plain")) fmt->style = EVAS_TEXT_STYLE_PLAIN;
1532 else if (!strcmp(p1, "shadow")) fmt->style = EVAS_TEXT_STYLE_SHADOW;
1533 else if (!strcmp(p1, "outline")) fmt->style = EVAS_TEXT_STYLE_OUTLINE;
1534 else if (!strcmp(p1, "soft_outline")) fmt->style = EVAS_TEXT_STYLE_SOFT_OUTLINE;
1535 else if (!strcmp(p1, "outline_shadow")) fmt->style = EVAS_TEXT_STYLE_OUTLINE_SHADOW;
1536 else if (!strcmp(p1, "outline_soft_shadow")) fmt->style = EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW;
1537 else if (!strcmp(p1, "glow")) fmt->style = EVAS_TEXT_STYLE_GLOW;
1538 else if (!strcmp(p1, "far_shadow")) fmt->style = EVAS_TEXT_STYLE_FAR_SHADOW;
1539 else if (!strcmp(p1, "soft_shadow")) fmt->style = EVAS_TEXT_STYLE_SOFT_SHADOW;
1540 else if (!strcmp(p1, "far_soft_shadow")) fmt->style = EVAS_TEXT_STYLE_FAR_SOFT_SHADOW;
1541 else fmt->style = EVAS_TEXT_STYLE_PLAIN;
1545 if (!strcmp(p2, "bottom_right")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT);
1546 else if (!strcmp(p2, "bottom")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM);
1547 else if (!strcmp(p2, "bottom_left")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT);
1548 else if (!strcmp(p2, "left")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT);
1549 else if (!strcmp(p2, "top_left")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT);
1550 else if (!strcmp(p2, "top")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP);
1551 else if (!strcmp(p2, "top_right")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT);
1552 else if (!strcmp(p2, "right")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT);
1553 else EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT);
1556 else if (cmd == tabstopsstr)
1558 fmt->tabstops = atoi(tmp_param);
1559 if (fmt->tabstops < 1) fmt->tabstops = 1;
1561 else if (cmd == linesizestr)
1563 fmt->linesize = atoi(tmp_param);
1564 fmt->linerelsize = 0.0;
1566 else if (cmd == linerelsizestr)
1568 char *endptr = NULL;
1569 double val = strtod(tmp_param, &endptr);
1572 while (*endptr && _is_white(*endptr))
1576 fmt->linerelsize = val / 100.0;
1578 if (fmt->linerelsize < 0.0) fmt->linerelsize = 0.0;
1582 else if (cmd == linegapstr)
1584 fmt->linegap = atoi(tmp_param);
1585 fmt->linerelgap = 0.0;
1587 else if (cmd == linerelgapstr)
1589 char *endptr = NULL;
1590 double val = strtod(tmp_param, &endptr);
1593 while (*endptr && _is_white(*endptr))
1597 fmt->linerelgap = val / 100.0;
1599 if (fmt->linerelgap < 0.0) fmt->linerelgap = 0.0;
1603 else if (cmd == itemstr)
1605 // itemstr == replacement object items in textblock - inline imges
1608 else if (cmd == linefillstr)
1610 char *endptr = NULL;
1611 double val = strtod(tmp_param, &endptr);
1614 while (*endptr && _is_white(*endptr))
1618 fmt->linefill = val / 100.0;
1619 if (fmt->linefill < 0.0) fmt->linefill = 0.0;
1623 else if (cmd == ellipsisstr)
1625 char *endptr = NULL;
1626 fmt->ellipsis = strtod(tmp_param, &endptr);
1627 if ((fmt->ellipsis < 0.0) || (fmt->ellipsis > 1.0))
1628 fmt->ellipsis = -1.0;
1631 Evas_Object_Textblock *o;
1633 o = (Evas_Object_Textblock *)(obj->object_data);
1634 o->have_ellipsis = 1;
1637 else if (cmd == passwordstr)
1639 if (!strcmp(tmp_param, "off"))
1641 else if (!strcmp(tmp_param, "on"))
1644 else if (cmd == underline_dash_widthstr)
1646 fmt->underline_dash_width = atoi(tmp_param);
1647 if (fmt->underline_dash_width <= 0) fmt->underline_dash_width = 1;
1649 else if (cmd == underline_dash_gapstr)
1651 fmt->underline_dash_gap = atoi(tmp_param);
1652 if (fmt->underline_dash_gap <= 0) fmt->underline_dash_gap = 1;
1658 * Returns @c EINA_TRUE if the item is a format parameter, @c EINA_FALSE
1661 * @param[in] item the item to check - Not NULL.
1664 _format_is_param(const char *item)
1666 if (strchr(item, '=')) return EINA_TRUE;
1672 * Parse the format item and populate key and val with the stringshares that
1673 * corrospond to the formats parsed.
1674 * It expects item to be of the structure:
1677 * @param[in] item the item to parse - Not NULL.
1678 * @param[out] key where to store the key at - Not NULL.
1679 * @param[out] val where to store the value at - Not NULL.
1682 _format_param_parse(const char *item, const char **key, const char **val)
1684 const char *start, *end, *quote;
1686 start = strchr(item, '=');
1687 *key = eina_stringshare_add_length(item, start - item);
1688 start++; /* Advance after the '=' */
1689 /* If we can find a quote, our new delimiter is a quote, not a space. */
1690 if ((quote = strchr(start, '\'')))
1693 end = strchr(start, '\'');
1697 end = strchr(start, ' ');
1700 /* Null terminate before the spaces */
1703 *val = eina_stringshare_add_length(start, end - start);
1707 *val = eina_stringshare_add(start);
1713 * This function parses the format passed in *s and advances s to point to the
1714 * next format item, while returning the current one as the return value.
1715 * @param s The current and returned position in the format string.
1716 * @return the current item parsed from the string.
1719 _format_parse(const char **s)
1722 const char *s1 = NULL, *s2 = NULL;
1723 Eina_Bool quote = EINA_FALSE;;
1726 if (*p == 0) return NULL;
1731 if (*p != ' ') s1 = p;
1741 if ((p > *s) && (p[-1] != '\\') && (!quote))
1743 if (*p == ' ') s2 = p;
1745 if (*p == 0) s2 = p;
1760 * Parse the format str and populate fmt with the formats found.
1762 * @param obj The evas object - Not NULL.
1763 * @param[out] fmt The format to populate - Not NULL.
1764 * @param[in] str the string to parse.- Not NULL.
1767 _format_fill(Evas_Object *obj, Evas_Object_Textblock_Format *fmt, const char *str)
1774 /* get rid of any spaces at the start of the string */
1775 while (*s == ' ') s++;
1777 while ((item = _format_parse(&s)))
1779 if (_format_is_param(item))
1781 const char *key = NULL, *val = NULL;
1783 _format_param_parse(item, &key, &val);
1784 _format_command(obj, fmt, key, val);
1785 eina_stringshare_del(key);
1786 eina_stringshare_del(val);
1790 /* immediate - not handled here */
1797 * Duplicate a format and return the duplicate.
1799 * @param obj The evas object - Not NULL.
1800 * @param[in] fmt The format to duplicate - Not NULL.
1801 * @return the copy of the format.
1803 static Evas_Object_Textblock_Format *
1804 _format_dup(Evas_Object *obj, const Evas_Object_Textblock_Format *fmt)
1806 Evas_Object_Textblock_Format *fmt2;
1808 fmt2 = calloc(1, sizeof(Evas_Object_Textblock_Format));
1809 memcpy(fmt2, fmt, sizeof(Evas_Object_Textblock_Format));
1811 fmt2->font.fdesc = evas_font_desc_ref(fmt->font.fdesc);
1813 if (fmt->font.source) fmt2->font.source = eina_stringshare_add(fmt->font.source);
1815 /* FIXME: just ref the font here... */
1816 fmt2->font.font = evas_font_load(obj->layer->evas, fmt2->font.fdesc,
1817 fmt2->font.source, (int)(((double) fmt2->font.size) * obj->cur.scale));
1828 * A pack of information that needed to be passed around in the layout engine,
1829 * packed for easier access.
1831 typedef struct _Ctxt Ctxt;
1836 Evas_Object_Textblock *o;
1838 Evas_Object_Textblock_Paragraph *paragraphs;
1839 Evas_Object_Textblock_Paragraph *par;
1840 Evas_Object_Textblock_Line *ln;
1843 Eina_List *format_stack;
1844 Evas_Object_Textblock_Format *fmt;
1849 int maxascent, maxdescent;
1850 int marginl, marginr;
1852 int underline_extend;
1853 int have_underline, have_underline2;
1854 double align, valign;
1855 Eina_Bool align_auto : 1;
1856 Eina_Bool width_changed : 1;
1859 static void _layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Text_Item *ti, Eina_List *rel);
1860 static void _text_item_update_sizes(Ctxt *c, Evas_Object_Textblock_Text_Item *ti);
1861 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);
1864 * Adjust the ascent/descent of the format and context.
1866 * @param maxascent The ascent to update - Not NUL.
1867 * @param maxdescent The descent to update - Not NUL.
1868 * @param fmt The format to adjust - NOT NULL.
1871 _layout_format_ascent_descent_adjust(const Evas_Object *obj,
1872 Evas_Coord *maxascent, Evas_Coord *maxdescent,
1873 Evas_Object_Textblock_Format *fmt)
1875 int ascent, descent;
1879 // ascent = c->ENFN->font_max_ascent_get(c->ENDT, fmt->font.font);
1880 // descent = c->ENFN->font_max_descent_get(c->ENDT, fmt->font.font);
1881 ascent = ENFN->font_ascent_get(ENDT, fmt->font.font);
1882 descent = ENFN->font_descent_get(ENDT, fmt->font.font);
1883 if (fmt->linesize > 0)
1885 if ((ascent + descent) < fmt->linesize)
1887 ascent = ((fmt->linesize * ascent) / (ascent + descent));
1888 descent = fmt->linesize - ascent;
1891 else if (fmt->linerelsize > 0.0)
1893 descent = descent * fmt->linerelsize;
1894 ascent = ascent * fmt->linerelsize;
1896 descent += fmt->linegap;
1897 descent += ((ascent + descent) * fmt->linerelgap);
1898 if (*maxascent < ascent) *maxascent = ascent;
1899 if (*maxdescent < descent) *maxdescent = descent;
1900 if (fmt->linefill > 0.0)
1904 dh = obj->cur.geometry.h - (*maxascent + *maxdescent);
1906 dh = fmt->linefill * dh;
1907 *maxdescent += dh / 2;
1908 *maxascent += dh - (dh / 2);
1909 // FIXME: set flag that says "if heigh changes - reformat"
1916 * Create a new line using the info from the format and update the format
1919 * @param c The context to work on - Not NULL.
1920 * @param fmt The format to use info from - NOT NULL.
1923 _layout_line_new(Ctxt *c, Evas_Object_Textblock_Format *fmt)
1925 c->ln = calloc(1, sizeof(Evas_Object_Textblock_Line));
1926 c->align = fmt->halign;
1927 c->align_auto = fmt->halign_auto;
1928 c->marginl = fmt->margin.l;
1929 c->marginr = fmt->margin.r;
1930 c->par->lines = (Evas_Object_Textblock_Line *)eina_inlist_append(EINA_INLIST_GET(c->par->lines), EINA_INLIST_GET(c->ln));
1932 c->maxascent = c->maxdescent = 0;
1933 c->ln->line_no = -1;
1934 c->ln->par = c->par;
1937 static inline Evas_Object_Textblock_Paragraph *
1938 _layout_find_paragraph_by_y(Evas_Object_Textblock *o, Evas_Coord y)
1940 Evas_Object_Textblock_Paragraph *start, *par;
1943 start = o->paragraphs;
1945 for (i = 0 ; i < TEXTBLOCK_PAR_INDEX_SIZE ; i++)
1947 if (!o->par_index[i] || (o->par_index[i]->y > y))
1951 start = o->par_index[i];
1954 EINA_INLIST_FOREACH(start, par)
1956 if ((par->y <= y) && (y < par->y + par->h))
1963 static inline Evas_Object_Textblock_Paragraph *
1964 _layout_find_paragraph_by_line_no(Evas_Object_Textblock *o, int line_no)
1966 Evas_Object_Textblock_Paragraph *start, *par;
1969 start = o->paragraphs;
1971 for (i = 0 ; i < TEXTBLOCK_PAR_INDEX_SIZE ; i++)
1973 if (!o->par_index[i] || (o->par_index[i]->line_no > line_no))
1977 start = o->par_index[i];
1980 EINA_INLIST_FOREACH(start, par)
1982 Evas_Object_Textblock_Paragraph *npar =
1983 (Evas_Object_Textblock_Paragraph *) EINA_INLIST_GET(par)->next;
1984 if ((par->line_no <= line_no) &&
1985 (!npar || (line_no < npar->line_no)))
1991 /* End of rbtree index functios */
1995 * Create a new layout paragraph.
1996 * If c->par is not NULL, the paragraph is appended/prepended according
1997 * to the append parameter. If it is NULL, the paragraph is appended at
1998 * the end of the list.
2000 * @param c The context to work on - Not NULL.
2001 * @param n the associated text node
2002 * @param append true to append, false to prpend.
2005 _layout_paragraph_new(Ctxt *c, Evas_Object_Textblock_Node_Text *n,
2008 Evas_Object_Textblock_Paragraph *rel_par = c->par;
2009 c->par = calloc(1, sizeof(Evas_Object_Textblock_Paragraph));
2010 if (append || !rel_par)
2011 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
2012 eina_inlist_append_relative(EINA_INLIST_GET(c->paragraphs),
2013 EINA_INLIST_GET(c->par),
2014 EINA_INLIST_GET(rel_par));
2016 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
2017 eina_inlist_prepend_relative(EINA_INLIST_GET(c->paragraphs),
2018 EINA_INLIST_GET(c->par),
2019 EINA_INLIST_GET(rel_par));
2022 c->par->text_node = n;
2025 c->par->line_no = -1;
2026 c->par->visible = 1;
2027 c->o->num_paragraphs++;
2033 * Update bidi paragraph props.
2035 * @param par The paragraph to update
2038 _layout_update_bidi_props(const Evas_Object_Textblock *o,
2039 Evas_Object_Textblock_Paragraph *par)
2043 const Eina_Unicode *text;
2044 int *segment_idxs = NULL;
2045 text = eina_ustrbuf_string_get(par->text_node->unicode);
2047 if (o->bidi_delimiters)
2048 segment_idxs = evas_bidi_segment_idxs_get(text, o->bidi_delimiters);
2050 evas_bidi_paragraph_props_unref(par->bidi_props);
2051 par->bidi_props = evas_bidi_paragraph_props_get(text,
2052 eina_ustrbuf_length_get(par->text_node->unicode),
2054 par->direction = EVAS_BIDI_PARAGRAPH_DIRECTION_IS_RTL(par->bidi_props) ?
2055 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
2056 par->is_bidi = !!par->bidi_props;
2057 if (segment_idxs) free(segment_idxs);
2065 * Free the visual lines in the paragraph (logical items are kept)
2068 _paragraph_clear(const Evas_Object *obj __UNUSED__,
2069 Evas_Object_Textblock_Paragraph *par)
2073 Evas_Object_Textblock_Line *ln;
2075 ln = (Evas_Object_Textblock_Line *) par->lines;
2076 par->lines = (Evas_Object_Textblock_Line *)eina_inlist_remove(EINA_INLIST_GET(par->lines), EINA_INLIST_GET(par->lines));
2083 * Free the layout paragraph and all of it's lines and logical items.
2086 _paragraph_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *par)
2088 Evas_Object_Textblock *o;
2089 o = (Evas_Object_Textblock *)(obj->object_data);
2090 _paragraph_clear(obj, par);
2093 Eina_List *i, *i_prev;
2094 Evas_Object_Textblock_Item *it;
2095 EINA_LIST_FOREACH_SAFE(par->logical_items, i, i_prev, it)
2097 _item_free(obj, NULL, it);
2099 eina_list_free(par->logical_items);
2102 if (par->bidi_props)
2103 evas_bidi_paragraph_props_unref(par->bidi_props);
2105 /* If we are the active par of the text node, set to NULL */
2106 if (par->text_node && (par->text_node->par == par))
2107 par->text_node->par = NULL;
2109 o->num_paragraphs--;
2116 * Clear all the paragraphs from the inlist pars.
2118 * @param obj the evas object - Not NULL.
2119 * @param pars the paragraphs to clean - Not NULL.
2122 _paragraphs_clear(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *pars)
2124 Evas_Object_Textblock_Paragraph *par;
2126 EINA_INLIST_FOREACH(EINA_INLIST_GET(pars), par)
2128 _paragraph_clear(obj, par);
2134 * Free the paragraphs from the inlist pars, the difference between this and
2135 * _paragraphs_clear is that the latter keeps the logical items and the par
2136 * items, while the former frees them as well.
2138 * @param obj the evas object - Not NULL.
2139 * @param pars the paragraphs to clean - Not NULL.
2142 _paragraphs_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *pars)
2144 Evas_Object_Textblock *o;
2145 o = (Evas_Object_Textblock *)(obj->object_data);
2147 o->num_paragraphs = 0;
2151 Evas_Object_Textblock_Paragraph *par;
2153 par = (Evas_Object_Textblock_Paragraph *) pars;
2154 pars = (Evas_Object_Textblock_Paragraph *)eina_inlist_remove(EINA_INLIST_GET(pars), EINA_INLIST_GET(par));
2155 _paragraph_free(obj, par);
2161 * Push fmt to the format stack, if fmt is NULL, will fush a default item.
2163 * @param c the context to work on - Not NULL.
2164 * @param fmt the format to push.
2165 * @see _layout_format_pop()
2167 static Evas_Object_Textblock_Format *
2168 _layout_format_push(Ctxt *c, Evas_Object_Textblock_Format *fmt,
2169 Evas_Object_Textblock_Node_Format *fnode)
2173 fmt = _format_dup(c->obj, fmt);
2174 c->format_stack = eina_list_prepend(c->format_stack, fmt);
2179 fmt = calloc(1, sizeof(Evas_Object_Textblock_Format));
2180 c->format_stack = eina_list_prepend(c->format_stack, fmt);
2183 fmt->halign_auto = EINA_TRUE;
2185 fmt->style = EVAS_TEXT_STYLE_PLAIN;
2188 fmt->linerelsize = 0.0;
2190 fmt->underline_dash_width = 6;
2191 fmt->underline_dash_gap = 2;
2192 fmt->linerelgap = 0.0;
2200 * Pop fmt to the format stack, if there's something in the stack free fmt
2201 * and set it to point to the next item instead, else return fmt.
2203 * @param c the context to work on - Not NULL.
2204 * @param format - the text of the format to free (assured to start with '-').
2205 * @return the next format in the stack, or format if there's none.
2206 * @see _layout_format_push()
2208 static Evas_Object_Textblock_Format *
2209 _layout_format_pop(Ctxt *c, const char *format)
2211 Evas_Object_Textblock_Format *fmt = eina_list_data_get(c->format_stack);
2213 if ((c->format_stack) && (c->format_stack->next))
2215 Eina_List *redo_nodes = NULL;
2217 /* Generic pop, should just pop. */
2218 if (((format[0] == ' ') && !format[1]) ||
2221 _format_unref_free(c->obj, fmt);
2223 eina_list_remove_list(c->format_stack, c->format_stack);
2227 size_t len = strlen(format);
2228 Eina_List *i, *i_next;
2229 /* Remove only the matching format. */
2230 EINA_LIST_FOREACH_SAFE(c->format_stack, i, i_next, fmt)
2232 /* Stop when we reach the base item */
2237 eina_list_remove_list(c->format_stack, c->format_stack);
2239 /* Make sure the ending tag matches the starting tag.
2240 * I.e whole of the ending tag matches the start of the
2241 * starting tag, and the starting tag's next char is either
2242 * NULL or white. Skip the starting '+'. */
2243 if (_FORMAT_IS_CLOSER_OF(
2244 fmt->fnode->orig_format, format, len))
2246 _format_unref_free(c->obj, fmt);
2251 redo_nodes = eina_list_prepend(redo_nodes, fmt->fnode);
2252 _format_unref_free(c->obj, fmt);
2257 /* Redo all the nodes needed to be redone */
2259 Evas_Object_Textblock_Node_Format *fnode;
2260 Eina_List *i, *i_next;
2262 EINA_LIST_FOREACH_SAFE(redo_nodes, i, i_next, fnode)
2264 /* FIXME: Actually do something with the new acquired padding,
2265 * the can be different and affect our padding! */
2266 Evas_Coord style_pad_l, style_pad_r, style_pad_t, style_pad_b;
2267 style_pad_l = style_pad_r = style_pad_t = style_pad_b = 0;
2268 redo_nodes = eina_list_remove_list(redo_nodes, i);
2269 fmt = eina_list_data_get(c->format_stack);
2270 _layout_do_format(c->obj, c, &fmt, fnode,
2271 &style_pad_l, &style_pad_r,
2272 &style_pad_t, &style_pad_b, EINA_FALSE);
2276 fmt = eina_list_data_get(c->format_stack);
2283 * Parse item and fill fmt with the item.
2285 * @param c the context to work on - Not NULL.
2286 * @param fmt the format to fill - not null.
2289 _layout_format_value_handle(Ctxt *c, Evas_Object_Textblock_Format *fmt, const char *item)
2291 const char *key = NULL, *val = NULL;
2293 _format_param_parse(item, &key, &val);
2294 if ((key) && (val)) _format_command(c->obj, fmt, key, val);
2295 if (key) eina_stringshare_del(key);
2296 if (val) eina_stringshare_del(val);
2297 c->align = fmt->halign;
2298 c->align_auto = fmt->halign_auto;
2299 c->marginl = fmt->margin.l;
2300 c->marginr = fmt->margin.r;
2303 #define VSIZE_FULL 0
2304 #define VSIZE_ASCENT 1
2312 * Get the current line's alignment from the context.
2314 * @param c the context to work on - Not NULL.
2316 static inline double
2317 _layout_line_align_get(Ctxt *c)
2320 if (c->align_auto && c->ln)
2322 if (c->ln->items && c->ln->items->text_node &&
2323 (c->ln->par->direction == EVAS_BIDI_DIRECTION_RTL))
2341 * Reorder the items in visual order
2343 * @param line the line to reorder
2346 _layout_line_reorder(Evas_Object_Textblock_Line *line)
2348 /*FIXME: do it a bit more efficient - not very efficient ATM. */
2349 Evas_Object_Textblock_Item *it;
2350 EvasBiDiStrIndex *v_to_l = NULL;
2355 if (line->items && line->items->text_node &&
2356 line->par->bidi_props)
2358 Evas_BiDi_Paragraph_Props *props;
2359 props = line->par->bidi_props;
2360 start = end = line->items->text_pos;
2362 /* Find the first and last positions in the line */
2364 EINA_INLIST_FOREACH(line->items, it)
2366 if (it->text_pos < start)
2368 start = it->text_pos;
2373 tlen = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
2374 _ITEM_TEXT(it)->text_props.text_len : 1;
2375 if (it->text_pos + tlen > end)
2377 end = it->text_pos + tlen;
2383 evas_bidi_props_reorder_line(NULL, start, len, props, &v_to_l);
2385 /* Update visual pos */
2387 Evas_Object_Textblock_Item *i;
2391 i->visual_pos = evas_bidi_position_logical_to_visual(
2392 v_to_l, len, i->text_pos - start);
2393 i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(i)->next;
2397 /*FIXME: not very efficient, sort the items arrays. Anyhow, should only
2398 * reorder if it's a bidi paragraph */
2400 Evas_Object_Textblock_Item *i, *j, *min;
2405 EINA_INLIST_FOREACH(i, j)
2407 if (j->visual_pos < min->visual_pos)
2414 line->items = (Evas_Object_Textblock_Item *) eina_inlist_remove(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min));
2415 line->items = (Evas_Object_Textblock_Item *) eina_inlist_prepend_relative(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min), EINA_INLIST_GET(i));
2418 i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(min)->next;
2423 if (v_to_l) free(v_to_l);
2425 EINA_INLIST_FOREACH(line->items, it)
2435 _layout_calculate_format_item_size(const Evas_Object *obj,
2436 const Evas_Object_Textblock_Format_Item *fi,
2437 Evas_Coord *maxascent, Evas_Coord *maxdescent,
2438 Evas_Coord *_y, Evas_Coord *_w, Evas_Coord *_h)
2440 /* Adjust sizes according to current line height/scale */
2450 p = strstr(s, " size=");
2454 if (sscanf(p, "%ix%i", &w, &h) == 2)
2456 w = w * obj->cur.scale;
2457 h = h * obj->cur.scale;
2462 p = strstr((char *) s, " relsize=");
2464 if (sscanf(p, "%ix%i", &w, &h) == 2)
2467 if (fi->vsize == VSIZE_FULL)
2469 sz = *maxdescent + *maxascent;
2471 else if (fi->vsize == VSIZE_ASCENT)
2492 if (h > (*maxdescent + *maxascent))
2494 *maxascent += h - (*maxdescent + *maxascent);
2498 *_y = -(h - *maxdescent);
2534 * Order the items in the line, update it's properties and update it's
2535 * corresponding paragraph.
2537 * @param c the context to work on - Not NULL.
2538 * @param fmt the format to use.
2539 * @param add_line true if we should create a line, false otherwise.
2542 _layout_line_finalize(Ctxt *c, Evas_Object_Textblock_Format *fmt)
2544 Evas_Object_Textblock_Item *it;
2547 /* If there are no text items yet, calc ascent/descent
2548 * according to the current format. */
2549 if (c->maxascent + c->maxdescent == 0)
2550 _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
2551 &c->maxdescent, fmt);
2553 /* Adjust all the item sizes according to the final line size,
2554 * and update the x positions of all the items of the line. */
2555 EINA_INLIST_FOREACH(c->ln->items, it)
2557 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
2559 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
2560 if (!fi->formatme) goto loop_advance;
2561 _layout_calculate_format_item_size(c->obj, fi, &c->maxascent,
2562 &c->maxdescent, &fi->y, &fi->parent.w, &fi->parent.h);
2563 fi->parent.adv = fi->parent.w;
2570 if ((it->x + it->adv) > c->ln->w) c->ln->w = it->x + it->adv;
2573 c->ln->y = (c->y - c->par->y) + c->o->style_pad.t;
2574 c->ln->h = c->maxascent + c->maxdescent;
2575 c->ln->baseline = c->maxascent;
2576 if (c->have_underline2)
2578 if (c->maxdescent < 4) c->underline_extend = 4 - c->maxdescent;
2580 else if (c->have_underline)
2582 if (c->maxdescent < 2) c->underline_extend = 2 - c->maxdescent;
2584 c->ln->line_no = c->line_no - c->ln->par->line_no;
2586 c->y += c->maxascent + c->maxdescent;
2589 c->ln->x = c->marginl + c->o->style_pad.l +
2591 c->o->style_pad.l - c->o->style_pad.r -
2592 c->marginl - c->marginr) * _layout_line_align_get(c));
2596 c->ln->x = c->marginl + c->o->style_pad.l;
2599 c->par->h = c->ln->y + c->ln->h;
2600 if (c->ln->w > c->par->w)
2601 c->par->w = c->ln->w;
2604 Evas_Coord new_wmax = c->ln->w +
2605 c->marginl + c->marginr - (c->o->style_pad.l + c->o->style_pad.r);
2606 if (new_wmax > c->wmax)
2613 * Create a new line and append it to the lines in the context.
2615 * @param c the context to work on - Not NULL.
2616 * @param fmt the format to use.
2617 * @param add_line true if we should create a line, false otherwise.
2620 _layout_line_advance(Ctxt *c, Evas_Object_Textblock_Format *fmt)
2622 _layout_line_finalize(c, fmt);
2623 _layout_line_new(c, fmt);
2628 * Create a new text layout item from the string and the format.
2630 * @param c the context to work on - Not NULL.
2631 * @param fmt the format to use.
2632 * @param str the string to use.
2633 * @param len the length of the string.
2635 static Evas_Object_Textblock_Text_Item *
2636 _layout_text_item_new(Ctxt *c __UNUSED__, Evas_Object_Textblock_Format *fmt)
2638 Evas_Object_Textblock_Text_Item *ti;
2640 ti = calloc(1, sizeof(Evas_Object_Textblock_Text_Item));
2641 ti->parent.format = fmt;
2642 ti->parent.format->ref++;
2643 ti->parent.type = EVAS_TEXTBLOCK_ITEM_TEXT;
2649 * Return the cutoff of the text in the text item.
2651 * @param c the context to work on - Not NULL.
2652 * @param fmt the format to use. - Not NULL.
2653 * @param it the item to check - Not null.
2654 * @return -1 if there is no cutoff (either because there is really none,
2655 * or because of an error), cutoff index on success.
2658 _layout_text_cutoff_get(Ctxt *c, Evas_Object_Textblock_Format *fmt,
2659 const Evas_Object_Textblock_Text_Item *ti)
2664 x = c->w - c->o->style_pad.l - c->o->style_pad.r - c->marginl -
2665 c->marginr - c->x - ti->x_adjustment;
2668 return c->ENFN->font_last_up_to_pos(c->ENDT, fmt->font.font,
2669 &ti->text_props, x, 0);
2676 * Split before cut, and strip if str[cut - 1] is a whitespace.
2678 * @param c the context to work on - Not NULL.
2679 * @param ti the item to cut - not null.
2680 * @param lti the logical list item of the item.
2681 * @param cut the cut index.
2682 * @return the second (newly created) item.
2684 static Evas_Object_Textblock_Text_Item *
2685 _layout_item_text_split_strip_white(Ctxt *c,
2686 Evas_Object_Textblock_Text_Item *ti, Eina_List *lti, size_t cut)
2688 const Eina_Unicode *ts;
2689 Evas_Object_Textblock_Text_Item *new_ti = NULL, *white_ti = NULL;
2691 ts = GET_ITEM_TEXT(ti);
2693 if (!IS_AT_END(ti, cut) && (ti->text_props.text_len > 0))
2695 new_ti = _layout_text_item_new(c, ti->parent.format);
2696 new_ti->parent.text_node = ti->parent.text_node;
2697 new_ti->parent.text_pos = ti->parent.text_pos + cut;
2698 new_ti->parent.merge = EINA_TRUE;
2700 evas_common_text_props_split(&ti->text_props,
2701 &new_ti->text_props, cut);
2702 _layout_text_add_logical_item(c, new_ti, lti);
2705 /* Strip the previous white if needed */
2706 if ((cut >= 1) && _is_white(ts[cut - 1]) && (ti->text_props.text_len > 0))
2710 size_t white_cut = cut - 1;
2711 white_ti = _layout_text_item_new(c, ti->parent.format);
2712 white_ti->parent.text_node = ti->parent.text_node;
2713 white_ti->parent.text_pos = ti->parent.text_pos + white_cut;
2714 white_ti->parent.merge = EINA_TRUE;
2715 white_ti->parent.visually_deleted = EINA_TRUE;
2717 evas_common_text_props_split(&ti->text_props,
2718 &white_ti->text_props, white_cut);
2719 _layout_text_add_logical_item(c, white_ti, lti);
2723 /* Mark this one as the visually deleted. */
2724 ti->parent.visually_deleted = EINA_TRUE;
2728 if (new_ti || white_ti)
2730 _text_item_update_sizes(c, ti);
2737 * Merge item2 into item1 and free item2.
2739 * @param c the context to work on - Not NULL.
2740 * @param item1 the item to copy to
2741 * @param item2 the item to copy from
2744 _layout_item_merge_and_free(Ctxt *c,
2745 Evas_Object_Textblock_Text_Item *item1,
2746 Evas_Object_Textblock_Text_Item *item2)
2748 evas_common_text_props_merge(&item1->text_props,
2749 &item2->text_props);
2751 _text_item_update_sizes(c, item1);
2753 item1->parent.merge = EINA_FALSE;
2754 item1->parent.visually_deleted = EINA_FALSE;
2756 _item_free(c->obj, NULL, _ITEM(item2));
2761 * Calculates an item's size.
2763 * @param c the context
2764 * @param it the item itself.
2767 _text_item_update_sizes(Ctxt *c, Evas_Object_Textblock_Text_Item *ti)
2769 int tw, th, inset, advw;
2770 const Evas_Object_Textblock_Format *fmt = ti->parent.format;
2771 int shad_sz = 0, shad_dst = 0, out_sz = 0;
2772 int dx = 0, minx = 0, maxx = 0, shx1, shx2;
2776 c->ENFN->font_string_size_get(c->ENDT, fmt->font.font,
2777 &ti->text_props, &tw, &th);
2780 inset = c->ENFN->font_inset_get(c->ENDT, fmt->font.font,
2784 advw = c->ENFN->font_h_advance_get(c->ENDT, fmt->font.font,
2788 /* These adjustments are calculated and thus heavily linked to those in
2789 * textblock_render!!! Don't change one without the other. */
2791 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC)
2793 case EVAS_TEXT_STYLE_SHADOW:
2796 case EVAS_TEXT_STYLE_OUTLINE_SHADOW:
2797 case EVAS_TEXT_STYLE_FAR_SHADOW:
2801 case EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW:
2806 case EVAS_TEXT_STYLE_FAR_SOFT_SHADOW:
2810 case EVAS_TEXT_STYLE_SOFT_SHADOW:
2814 case EVAS_TEXT_STYLE_GLOW:
2815 case EVAS_TEXT_STYLE_SOFT_OUTLINE:
2818 case EVAS_TEXT_STYLE_OUTLINE:
2824 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_SHADOW_DIRECTION)
2826 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT:
2827 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT:
2828 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT:
2831 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT:
2832 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT:
2833 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT:
2836 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP:
2837 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM:
2844 shx1 = dx * shad_dst;
2846 shx2 = dx * shad_dst;
2848 if (shx1 < minx) minx = shx1;
2849 if (shx2 > maxx) maxx = shx2;
2851 ti->x_adjustment = maxx - minx;
2854 ti->parent.w = tw + ti->x_adjustment;
2856 ti->parent.adv = advw;
2862 * Adds the item to the list, updates the item's properties (e.g, x,w,h)
2864 * @param c the context
2865 * @param it the item itself.
2866 * @param rel item ti will be appened after, NULL = last.
2869 _layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Text_Item *ti,
2872 _text_item_update_sizes(c, ti);
2874 c->par->logical_items = eina_list_append_relative_list(
2875 c->par->logical_items, ti, rel);
2880 * Appends the text from node n starting at start ending at off to the layout.
2881 * It uses the fmt for the formatting.
2883 * @param c the current context- NOT NULL.
2884 * @param fmt the format to use.
2885 * @param n the text node. - Not null.
2886 * @param start the start position. - in range.
2887 * @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.
2888 * @param repch a replacement char to print instead of the original string, for example, * when working with passwords.
2891 _layout_text_append(Ctxt *c, Evas_Object_Textblock_Format *fmt, Evas_Object_Textblock_Node_Text *n, int start, int off, const char *repch)
2893 const Eina_Unicode *str = EINA_UNICODE_EMPTY_STRING;
2894 const Eina_Unicode *tbase;
2895 Evas_Object_Textblock_Text_Item *ti;
2897 Eina_Unicode urepch = 0;
2899 /* prepare a working copy of the string, either filled by the repch or
2900 * filled with the true values */
2906 /* Figure out if we want to bail, work with an empty string,
2907 * or continue with a slice of the passed string */
2908 len = eina_ustrbuf_length_get(n->unicode);
2909 if (off == 0) return;
2910 else if (off < 0) off = len - start;
2916 else if ((start == 0) && (off == 0) && (orig_off == -1))
2918 /* Special case that means that we need to add an empty
2920 str = EINA_UNICODE_EMPTY_STRING;
2923 else if ((start >= len) || (start + off > len))
2928 /* If we work with a replacement char, create a string which is the same
2929 * but with replacement chars instead of regular chars. */
2930 if ((fmt->password) && (repch) && (eina_ustrbuf_length_get(n->unicode)))
2935 tbase = str = ptr = alloca((off + 1) * sizeof(Eina_Unicode));
2937 urepch = eina_unicode_utf8_get_next(repch, &ind);
2938 for (i = 0 ; i < off; ptr++, i++)
2942 /* Use the string, just cut the relevant parts */
2945 str = eina_ustrbuf_string_get(n->unicode) + start;
2954 /* If there's no parent text node, only create an empty item */
2957 ti = _layout_text_item_new(c, fmt);
2958 ti->parent.text_node = NULL;
2959 ti->parent.text_pos = 0;
2960 _layout_text_add_logical_item(c, ti, NULL);
2967 Evas_Font_Instance *script_fi = NULL;
2968 int script_len, tmp_cut;
2969 Evas_Script_Type script;
2971 script_len = cur_len;
2973 tmp_cut = evas_common_language_script_end_of_run_get(str,
2974 c->par->bidi_props, start + str - tbase, script_len);
2977 script_len = tmp_cut;
2979 cur_len -= script_len;
2981 script = evas_common_language_script_type_get(str, script_len);
2984 while (script_len > 0)
2986 Evas_Font_Instance *cur_fi = NULL;
2987 int run_len = script_len;
2988 ti = _layout_text_item_new(c, fmt);
2989 ti->parent.text_node = n;
2990 ti->parent.text_pos = start + str - tbase;
2992 if (ti->parent.format->font.font)
2994 run_len = c->ENFN->font_run_end_get(c->ENDT,
2995 ti->parent.format->font.font, &script_fi, &cur_fi,
2996 script, str, script_len);
2999 evas_common_text_props_bidi_set(&ti->text_props,
3000 c->par->bidi_props, ti->parent.text_pos);
3001 evas_common_text_props_script_set(&ti->text_props, script);
3005 c->ENFN->font_text_props_info_create(c->ENDT,
3006 cur_fi, str, &ti->text_props, c->par->bidi_props,
3007 ti->parent.text_pos, run_len, EVAS_TEXT_PROPS_MODE_SHAPE);
3010 script_len -= run_len;
3012 _layout_text_add_logical_item(c, ti, NULL);
3019 * Add a format item from the format node n and the item item.
3021 * @param c the current context- NOT NULL.
3022 * @param n the source format node - not null.
3023 * @param item the format text.
3025 * @return the new format item.
3027 static Evas_Object_Textblock_Format_Item *
3028 _layout_format_item_add(Ctxt *c, Evas_Object_Textblock_Node_Format *n, const char *item, Evas_Object_Textblock_Format *fmt)
3030 Evas_Object_Textblock_Format_Item *fi;
3032 fi = calloc(1, sizeof(Evas_Object_Textblock_Format_Item));
3033 fi->item = eina_stringshare_add(item);
3034 fi->parent.type = EVAS_TEXTBLOCK_ITEM_FORMAT;
3035 fi->parent.format = fmt;
3036 fi->parent.format->ref++;
3037 c->par->logical_items = eina_list_append(c->par->logical_items, fi);
3040 fi->parent.text_node = n->text_node;
3041 /* FIXME: make it more efficient */
3042 fi->parent.text_pos = _evas_textblock_node_format_pos_get(n);
3044 fi->bidi_dir = (evas_bidi_is_rtl_char(
3047 fi->parent.text_pos)) ?
3048 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
3050 fi->bidi_dir = EVAS_BIDI_DIRECTION_LTR;
3058 * Should be call after we finish filling a format.
3062 _format_finalize(Evas_Object *obj, Evas_Object_Textblock_Format *fmt)
3066 of = fmt->font.font;
3068 fmt->font.font = evas_font_load(obj->layer->evas, fmt->font.fdesc,
3069 fmt->font.source, (int)(((double) fmt->font.size) * obj->cur.scale));
3070 if (of) evas_font_free(obj->layer->evas, of);
3075 * Returns true if the item is a tab
3076 * @def _IS_TAB(item)
3078 #define _IS_TAB(item) \
3079 (!strcmp(item, "tab") || !strcmp(item, "\t") || !strcmp(item, "\\t"))
3082 * Returns true if the item is a line spearator, false otherwise
3083 * @def _IS_LINE_SEPARATOR(item)
3085 #define _IS_LINE_SEPARATOR(item) \
3086 (!strcmp(item, "br") || !strcmp(item, "\n") || !strcmp(item, "\\n"))
3089 * Returns true if the item is a paragraph separator, false otherwise
3090 * @def _IS_PARAGRAPH_SEPARATOR(item)
3092 #define _IS_PARAGRAPH_SEPARATOR_SIMPLE(item) \
3093 (!strcmp(item, "ps"))
3096 * Returns true if the item is a paragraph separator, false otherwise
3097 * takes legacy mode into account.
3098 * @def _IS_PARAGRAPH_SEPARATOR(item)
3100 #define _IS_PARAGRAPH_SEPARATOR(o, item) \
3101 (_IS_PARAGRAPH_SEPARATOR_SIMPLE(item) || \
3102 (o->legacy_newline && _IS_LINE_SEPARATOR(item))) /* Paragraph separator */
3106 * Handles a format by processing a format node. It returns the relevant format
3107 * through _fmt and updates the padding through style_pad_*. If needed,
3108 * it creates a format item.
3110 * @param obj the evas object - NOT NULL.
3111 * @param c the current context- NOT NULL.
3112 * @param _fmt the format that holds the result.
3113 * @param n the source format node - not null.
3114 * @param style_pad_l the pad to update.
3115 * @param style_pad_r the pad to update.
3116 * @param style_pad_t the pad to update.
3117 * @param style_pad_b the pad to update.
3118 * @param create_item Create a new format item if true, only process otherwise.
3121 _layout_do_format(const Evas_Object *obj __UNUSED__, Ctxt *c,
3122 Evas_Object_Textblock_Format **_fmt, Evas_Object_Textblock_Node_Format *n,
3123 int *style_pad_l, int *style_pad_r, int *style_pad_t, int *style_pad_b,
3124 Eina_Bool create_item)
3126 Evas_Object_Textblock_Format *fmt = *_fmt;
3127 /* FIXME: comment the algo */
3134 if (!strncmp(s, "item ", 5))
3137 // item size=20x10 href=name
3138 // item relsize=20x10 href=name
3139 // item abssize=20x10 href=name
3141 // optional arguments:
3145 // size == item size (modifies line size) - can be multiplied by
3147 // relsize == relative size (height is current font height, width
3148 // modified accordingly keeping aspect)
3149 // abssize == absolute size (modifies line size) - never mulitplied by
3151 // href == name of item - to be found and matched later and used for
3153 Evas_Object_Textblock_Format_Item *fi;
3155 int vsize = 0, size = 0;
3159 //href = strstr(s, " href=");
3160 p = strstr(s, " vsize=");
3164 if (!strncmp(p, "full", 4)) vsize = VSIZE_FULL;
3165 else if (!strncmp(p, "ascent", 6)) vsize = VSIZE_ASCENT;
3167 p = strstr(s, " size=");
3171 if (sscanf(p, "%ix%i", &w, &h) == 2)
3173 /* this is handled somewhere else because it depends
3174 * on the current scaling factor of the object which
3175 * may change and break because the results of this
3176 * function are cached */
3182 p = strstr(s, " absize=");
3186 if (sscanf(p, "%ix%i", &w, &h) == 2)
3193 p = strstr(s, " relsize=");
3196 /* this is handled somewhere else because it depends
3197 * on the line it resides in, which is not defined
3198 * at this point and will change anyway, which will
3199 * break because the results of this function are
3208 fi = _layout_format_item_add(c, n, s, fmt);
3212 /* For formats items it's usually
3213 the same, we don't handle the
3214 special cases yet. */
3215 fi->parent.w = fi->parent.adv = w;
3218 /* Not sure if it's the best handling, but will do it for now. */
3219 fmt = _layout_format_push(c, fmt, n);
3225 Eina_Bool push_fmt = EINA_FALSE;
3226 if (n->opener && !n->own_closer)
3228 fmt = _layout_format_push(c, fmt, n);
3229 push_fmt = EINA_TRUE;
3231 else if (!n->opener)
3233 fmt = _layout_format_pop(c, n->orig_format);
3235 while ((item = _format_parse(&s)))
3237 if (_format_is_param(item))
3239 /* Only handle it if it's a push format, otherwise,
3240 * don't let overwrite the format stack.. */
3243 _layout_format_value_handle(c, fmt, item);
3246 else if (create_item)
3248 if ((_IS_PARAGRAPH_SEPARATOR(c->o, item)) ||
3249 (_IS_LINE_SEPARATOR(item)))
3251 Evas_Object_Textblock_Format_Item *fi;
3253 fi = _layout_format_item_add(c, n, item, fmt);
3255 fi->parent.w = fi->parent.adv = 0;
3257 else if (_IS_TAB(item))
3259 Evas_Object_Textblock_Format_Item *fi;
3261 fi = _layout_format_item_add(c, n, item, fmt);
3262 fi->parent.w = fi->parent.adv = fmt->tabstops;
3267 _format_finalize(c->obj, fmt);
3271 Evas_Coord pad_l, pad_r, pad_t, pad_b;
3272 pad_l = pad_r = pad_t = pad_b = 0;
3273 evas_text_style_pad_get(fmt->style, &pad_l, &pad_r, &pad_t, &pad_b);
3274 if (pad_l > *style_pad_l) *style_pad_l = pad_l;
3275 if (pad_r > *style_pad_r) *style_pad_r = pad_r;
3276 if (pad_t > *style_pad_t) *style_pad_t = pad_t;
3277 if (pad_b > *style_pad_b) *style_pad_b = pad_b;
3280 if (fmt->underline2)
3281 c->have_underline2 = 1;
3282 else if (fmt->underline || fmt->underline_dash)
3283 c->have_underline = 1;
3288 _layout_update_par(Ctxt *c)
3290 Evas_Object_Textblock_Paragraph *last_par;
3291 last_par = (Evas_Object_Textblock_Paragraph *)
3292 EINA_INLIST_GET(c->par)->prev;
3295 c->par->y = last_par->y + last_par->h;
3303 /* -1 means no wrap */
3305 _layout_get_charwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3306 const Evas_Object_Textblock_Item *it, size_t line_start,
3311 size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
3312 /* Currently not being used, because it doesn't contain relevant
3317 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3320 wrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it));
3324 uwrap = (size_t) wrap + it->text_pos;
3328 if ((uwrap == line_start) && (it->type == EVAS_TEXTBLOCK_ITEM_TEXT))
3330 uwrap = it->text_pos +
3331 (size_t) evas_common_text_props_cluster_next(
3332 &_ITEM_TEXT(it)->text_props, wrap);
3334 if ((uwrap <= line_start) || (uwrap > len))
3340 /* -1 means no wrap */
3341 #ifdef HAVE_LINEBREAK
3343 /* Allow break means: if we can break after the current char */
3344 #define ALLOW_BREAK(i) \
3345 (breaks[i] <= LINEBREAK_ALLOWBREAK)
3349 #define ALLOW_BREAK(i) \
3354 _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3355 const Evas_Object_Textblock_Item *it, Eina_Bool mixed_wrap,
3356 size_t line_start, const char *breaks)
3358 Eina_Bool wrap_after = EINA_FALSE;
3361 const Eina_Unicode *str = eina_ustrbuf_string_get(
3362 it->text_node->unicode);
3363 int item_start = it->text_pos;
3364 size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
3365 #ifndef HAVE_LINEBREAK
3366 /* Not used without liblinebreak ATM. */
3372 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3375 swrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it));
3376 /* Avoiding too small textblocks to even contain one char.
3377 * FIXME: This can cause breaking inside ligatures. */
3382 orig_wrap = wrap = swrap + item_start;
3385 if (wrap > line_start)
3387 /* The wrapping point found is the first char of the next string
3388 the rest works on the last char of the previous string.
3389 If it's a whitespace, then it's ok, and no need to go back
3390 because we'll remove it anyway. */
3391 if (!_is_white(str[wrap]))
3392 MOVE_PREV_UNTIL(line_start, wrap);
3393 /* If there's a breakable point inside the text, scan backwards until
3395 while (wrap > line_start)
3397 if (ALLOW_BREAK(wrap))
3402 if ((wrap > line_start) ||
3403 ((wrap == line_start) && (ALLOW_BREAK(wrap)) && (wrap < len)))
3405 /* We found a suitable wrapping point, break here. */
3406 MOVE_NEXT_UNTIL(len, wrap);
3413 return ((orig_wrap >= line_start) && (orig_wrap < len)) ?
3414 ((int) orig_wrap) : -1;
3418 /* Scan forward to find the next wrapping point */
3420 wrap_after = EINA_TRUE;
3425 /* If we need to find the position after the cutting point */
3426 if ((wrap == line_start) || (wrap_after))
3430 return _layout_get_charwrap(c, fmt, it,
3431 line_start, breaks);
3437 if (ALLOW_BREAK(wrap))
3443 if ((wrap < len) && (wrap > line_start))
3445 MOVE_NEXT_UNTIL(len, wrap);
3458 /* -1 means no wrap */
3460 _layout_get_wordwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3461 const Evas_Object_Textblock_Item *it, size_t line_start,
3464 return _layout_get_word_mixwrap_common(c, fmt, it, EINA_FALSE, line_start,
3468 /* -1 means no wrap */
3470 _layout_get_mixedwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3471 const Evas_Object_Textblock_Item *it, size_t line_start,
3474 return _layout_get_word_mixwrap_common(c, fmt, it, EINA_TRUE, line_start,
3478 /* Should be moved inside _layout_ellipsis_item_new once we fix the hack in
3479 * textblock render */
3480 static const Eina_Unicode _ellip_str[2] = { 0x2026, '\0' };
3482 static Evas_Object_Textblock_Text_Item *
3483 _layout_ellipsis_item_new(Ctxt *c, const Evas_Object_Textblock_Item *cur_it)
3485 Evas_Object_Textblock_Text_Item *ellip_ti;
3486 Evas_Script_Type script;
3487 Evas_Font_Instance *script_fi = NULL, *cur_fi;
3488 size_t len = 1; /* The length of _ellip_str */
3490 /* We can free it here, cause there's only one ellipsis item per tb. */
3491 if (c->o->ellip_ti) _item_free(c->obj, NULL, _ITEM(c->o->ellip_ti));
3492 c->o->ellip_ti = ellip_ti = _layout_text_item_new(c,
3493 eina_list_data_get(eina_list_last(c->format_stack)));
3494 ellip_ti->parent.text_node = cur_it->text_node;
3495 ellip_ti->parent.text_pos = cur_it->text_pos;
3496 script = evas_common_language_script_type_get(_ellip_str, len);
3498 evas_common_text_props_bidi_set(&ellip_ti->text_props,
3499 c->par->bidi_props, ellip_ti->parent.text_pos);
3500 evas_common_text_props_script_set (&ellip_ti->text_props, script);
3502 if (ellip_ti->parent.format->font.font)
3504 /* It's only 1 char anyway, we don't need the run end. */
3505 (void) c->ENFN->font_run_end_get(c->ENDT,
3506 ellip_ti->parent.format->font.font, &script_fi, &cur_fi,
3507 script, _ellip_str, len);
3509 c->ENFN->font_text_props_info_create(c->ENDT,
3510 cur_fi, _ellip_str, &ellip_ti->text_props,
3511 c->par->bidi_props, ellip_ti->parent.text_pos, len, EVAS_TEXT_PROPS_MODE_SHAPE);
3514 _text_item_update_sizes(c, ellip_ti);
3516 if (cur_it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3518 ellip_ti->parent.text_pos += _ITEM_TEXT(cur_it)->text_props.text_len
3523 ellip_ti->parent.text_pos++;
3534 _layout_handle_ellipsis(Ctxt *c, Evas_Object_Textblock_Item *it, Eina_List *i)
3536 Evas_Object_Textblock_Text_Item *ellip_ti;
3537 Evas_Object_Textblock_Item *last_it;
3540 ellip_ti = _layout_ellipsis_item_new(c, it);
3544 c->w -= ellip_ti->parent.w;
3546 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3548 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
3550 wrap = _layout_text_cutoff_get(c, last_it->format, ti);
3551 if ((wrap > 0) && !IS_AT_END(ti, (size_t) wrap))
3553 _layout_item_text_split_strip_white(c, ti, i, wrap);
3555 else if ((wrap == 0) && (c->ln->items))
3557 last_it = _ITEM(EINA_INLIST_GET(c->ln->items)->last);
3560 else if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3562 /* We don't want to add this format item. */
3567 c->w += ellip_ti->parent.w;
3568 /* If we should add this item, do it */
3571 c->ln->items = (Evas_Object_Textblock_Item *)
3572 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3573 EINA_INLIST_GET(it));
3574 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3576 Evas_Object_Textblock_Format_Item *fi;
3577 fi = _ITEM_FORMAT(it);
3581 c->ln->items = (Evas_Object_Textblock_Item *)
3582 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3583 EINA_INLIST_GET(_ITEM(ellip_ti)));
3584 _layout_line_finalize(c, ellip_ti->parent.format);
3589 _layout_paragraph_reorder_lines(Evas_Object_Textblock_Paragraph *par)
3591 Evas_Object_Textblock_Line *ln;
3593 EINA_INLIST_FOREACH(EINA_INLIST_GET(par->lines), ln)
3595 _layout_line_reorder(ln);
3601 _layout_paragraph_render(Evas_Object_Textblock *o,
3602 Evas_Object_Textblock_Paragraph *par)
3606 par->rendered = EINA_TRUE;
3611 _layout_update_bidi_props(o, par);
3612 _layout_paragraph_reorder_lines(par);
3613 /* Clear the bidi props because we don't need them anymore. */
3614 if (par->bidi_props)
3616 evas_bidi_paragraph_props_unref(par->bidi_props);
3617 par->bidi_props = NULL;
3625 /* 0 means go ahead, 1 means break without an error, 2 means
3626 * break with an error, should probably clean this a bit (enum/macro)
3629 _layout_par(Ctxt *c)
3631 Evas_Object_Textblock_Item *it;
3635 char *line_breaks = NULL;
3637 if (!c->par->logical_items)
3640 /* We want to show it. */
3641 c->par->visible = 1;
3643 /* Check if we need to skip this paragraph because it's already layouted
3644 * correctly, and mark handled nodes as dirty. */
3645 c->par->line_no = c->line_no;
3647 if (c->par->text_node)
3649 /* Skip this paragraph if width is the same, there is no ellipsis
3650 * and we aren't just calculating. */
3651 if (!c->par->text_node->is_new && !c->par->text_node->dirty &&
3652 !c->width_changed && c->par->lines &&
3653 !c->o->have_ellipsis)
3655 Evas_Object_Textblock_Line *ln;
3656 /* Update c->line_no */
3657 ln = (Evas_Object_Textblock_Line *)
3658 EINA_INLIST_GET(c->par->lines)->last;
3660 c->line_no = c->par->line_no + ln->line_no + 1;
3663 c->par->text_node->dirty = EINA_FALSE;
3664 c->par->text_node->is_new = EINA_FALSE;
3665 c->par->rendered = EINA_FALSE;
3667 /* Merge back and clear the paragraph */
3669 Eina_List *itr, *itr_next;
3670 Evas_Object_Textblock_Item *ititr, *prev_it = NULL;
3671 _paragraph_clear(c->obj, c->par);
3672 EINA_LIST_FOREACH_SAFE(c->par->logical_items, itr, itr_next, ititr)
3674 if (ititr->merge && prev_it &&
3675 (prev_it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
3676 (ititr->type == EVAS_TEXTBLOCK_ITEM_TEXT))
3678 _layout_item_merge_and_free(c, _ITEM_TEXT(prev_it),
3680 c->par->logical_items =
3681 eina_list_remove_list(c->par->logical_items, itr);
3693 it = _ITEM(eina_list_data_get(c->par->logical_items));
3694 _layout_line_new(c, it->format);
3695 /* We walk on our own because we want to be able to add items from
3696 * inside the list and then walk them on the next iteration. */
3697 for (i = c->par->logical_items ; i ; )
3701 it = _ITEM(eina_list_data_get(i));
3702 /* Skip visually deleted items */
3703 if (it->visually_deleted)
3705 i = eina_list_next(i);
3709 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3711 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
3712 _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
3713 &c->maxdescent, ti->parent.format);
3717 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
3720 /* If there are no text items yet, calc ascent/descent
3721 * according to the current format. */
3722 if (c->maxascent + c->maxdescent == 0)
3723 _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
3724 &c->maxdescent, it->format);
3726 _layout_calculate_format_item_size(c->obj, fi, &c->maxascent,
3727 &c->maxdescent, &fi->y, &fi->parent.w, &fi->parent.h);
3728 fi->parent.adv = fi->parent.w;
3733 /* Check if we need to wrap, i.e the text is bigger than the width,
3734 or we already found a wrap point. */
3736 (((c->x + it->adv) >
3737 (c->w - c->o->style_pad.l - c->o->style_pad.r -
3738 c->marginl - c->marginr)) || (wrap > 0)))
3740 /* Handle ellipsis here. If we don't have more width left
3741 * and no height left, or no more width left and no wrapping. */
3742 if ((it->format->ellipsis == 1.0) && (c->h >= 0) &&
3743 ((2 * it->h + c->y >
3744 c->h - c->o->style_pad.t - c->o->style_pad.b) ||
3745 (!it->format->wrap_word && !it->format->wrap_char &&
3746 !it->format->wrap_mixed)))
3748 _layout_handle_ellipsis(c, it, i);
3752 /* If we want to wrap and it's worth checking for wrapping
3753 * (i.e there's actually text). */
3754 else if ((it->format->wrap_word || it->format->wrap_char ||
3755 it->format->wrap_mixed) && it->text_node)
3760 it_len = (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) ?
3761 1 : _ITEM_TEXT(it)->text_props.text_len;
3764 #ifdef HAVE_LINEBREAK
3765 /* If we haven't calculated the linebreaks yet,
3769 /* Only relevant in those cases */
3770 if (it->format->wrap_word || it->format->wrap_mixed)
3773 lang = (it->format->font.fdesc) ?
3774 it->format->font.fdesc->lang : "";
3776 eina_ustrbuf_length_get(
3777 it->text_node->unicode);
3778 line_breaks = malloc(len);
3779 set_linebreaks_utf32((const utf32_t *)
3780 eina_ustrbuf_string_get(
3781 it->text_node->unicode),
3782 len, lang, line_breaks);
3787 line_start = c->ln->items->text_pos;
3789 line_start = it->text_pos;
3792 /* If we don't already have a wrap point from before */
3795 if (it->format->wrap_word)
3796 wrap = _layout_get_wordwrap(c, it->format, it,
3797 line_start, line_breaks);
3798 else if (it->format->wrap_char)
3799 wrap = _layout_get_charwrap(c, it->format, it,
3800 line_start, line_breaks);
3801 else if (it->format->wrap_mixed)
3802 wrap = _layout_get_mixedwrap(c, it->format, it,
3803 line_start, line_breaks);
3808 /* If it's before the item, rollback and apply.
3809 if it's in the item, cut.
3810 If it's after the item, delay the cut */
3813 size_t uwrap = (size_t) wrap;
3814 if (uwrap < it->text_pos)
3816 /* Rollback latest additions, and cut that
3818 i = eina_list_prev(i);
3819 it = eina_list_data_get(i);
3820 while (uwrap < it->text_pos)
3822 c->ln->items = _ITEM(
3824 EINA_INLIST_GET(c->ln->items),
3825 EINA_INLIST_GET(it)));
3826 i = eina_list_prev(i);
3827 it = eina_list_data_get(i);
3830 c->ln->items = _ITEM(
3832 EINA_INLIST_GET(c->ln->items),
3833 EINA_INLIST_GET(it)));
3836 /* If it points to the end, it means the previous
3837 * char is a whitespace we should remove, so this
3838 * is a wanted cutting point. */
3839 else if (uwrap > it->text_pos + it_len)
3841 /* FIXME: Should redo the ellipsis handling.
3842 * If we can do ellipsis, just cut here. */
3843 if (it->format->ellipsis == 1.0)
3845 _layout_handle_ellipsis(c, it, i);
3851 /* Delay the cut in a smart way i.e use the
3852 item_pos as the line_start, because
3853 there's already no cut before*/
3858 wrap -= it->text_pos; /* Cut here */
3863 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3865 _layout_item_text_split_strip_white(c,
3866 _ITEM_TEXT(it), i, wrap);
3871 /* Should wrap before the item */
3874 _layout_line_advance(c, it->format);
3881 if (!redo_item && !it->visually_deleted)
3883 c->ln->items = (Evas_Object_Textblock_Item *)
3884 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3885 EINA_INLIST_GET(it));
3886 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3888 Evas_Object_Textblock_Format_Item *fi;
3889 fi = _ITEM_FORMAT(it);
3891 /* If it's a newline, and we are not in newline compat
3892 * mode, or we are in newline compat mode, and this is
3893 * not used as a paragraph separator, advance */
3894 if (fi->item && _IS_LINE_SEPARATOR(fi->item) &&
3895 (!c->o->legacy_newline ||
3902 i = eina_list_next(i);
3906 /* Each line is according to the first item in it, and here
3907 * i is already the next item (or the current if we redo it) */
3910 it = _ITEM(eina_list_data_get(i));
3912 _layout_line_advance(c, it->format);
3917 /* Here 'it' is the last format used */
3918 _layout_line_finalize(c, it->format);
3922 #ifdef HAVE_LINEBREAK
3932 * Invalidate text nodes according to format changes
3933 * This goes through all the new format changes and marks the text nodes
3934 * that should be invalidated because of format changes.
3936 * @param c the working context.
3939 _format_changes_invalidate_text_nodes(Ctxt *c)
3941 Evas_Object_Textblock_Node_Format *fnode = c->o->format_nodes;
3942 Evas_Object_Textblock_Node_Text *start_n = NULL;
3943 Eina_List *fstack = NULL;
3949 const char *fstr = fnode->orig_format;
3950 /* balance < 0 means we gave up and everything should be
3952 if (fnode->opener && !fnode->own_closer)
3956 start_n = fnode->text_node;
3957 fstack = eina_list_prepend(fstack, fnode);
3959 else if (!fnode->opener)
3962 fstr_len = strlen(fstr);
3963 /* Generic popper, just pop */
3964 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
3966 fstack = eina_list_remove_list(fstack, fstack);
3969 /* Find the matching format and pop it, if the matching format
3970 * is out format, i.e the last one, pop and break. */
3974 Evas_Object_Textblock_Node_Format *fnode2;
3975 EINA_LIST_FOREACH(fstack, i, fnode2)
3977 if (_FORMAT_IS_CLOSER_OF(
3978 fnode2->orig_format, fstr, fstr_len))
3980 fstack = eina_list_remove_list(fstack, i);
3989 Evas_Object_Textblock_Node_Text *f_tnode =
3993 start_n->dirty = EINA_TRUE;
3994 if (start_n == f_tnode)
3997 _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
4002 else if (!fnode->visible)
4007 /* if we don't already have a starting point, use the
4008 * current paragraph. */
4010 start_n = fnode->text_node;
4014 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
4021 start_n->dirty = EINA_TRUE;
4022 start_n = _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
4028 /** FIXME: Document */
4030 _layout_pre(Ctxt *c, int *style_pad_l, int *style_pad_r, int *style_pad_t,
4033 Evas_Object *obj = c->obj;
4034 Evas_Object_Textblock *o = c->o;
4035 /* Mark text nodes as dirty if format have changed. */
4036 if (c->o->format_changed)
4038 _format_changes_invalidate_text_nodes(c);
4041 if (o->content_changed)
4043 Evas_Object_Textblock_Node_Text *n;
4044 c->o->have_ellipsis = 0;
4045 c->par = c->paragraphs = o->paragraphs;
4046 /* Go through all the text nodes to create the logical layout */
4047 EINA_INLIST_FOREACH(c->o->text_nodes, n)
4049 Evas_Object_Textblock_Node_Format *fnode;
4053 /* If it's not a new paragraph, either update it or skip it.
4054 * Remove all the paragraphs that were deleted */
4057 /* Remove all the deleted paragraphs at this point */
4058 while (c->par->text_node != n)
4060 Evas_Object_Textblock_Paragraph *tmp_par =
4061 (Evas_Object_Textblock_Paragraph *)
4062 EINA_INLIST_GET(c->par)->next;
4064 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4065 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
4066 EINA_INLIST_GET(c->par));
4067 _paragraph_free(obj, c->par);
4072 /* If it's dirty, remove and recreate, if it's clean,
4073 * skip to the next. */
4076 Evas_Object_Textblock_Paragraph *prev_par = c->par;
4078 _layout_paragraph_new(c, n, EINA_TRUE);
4080 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4081 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
4082 EINA_INLIST_GET(prev_par));
4083 _paragraph_free(obj, prev_par);
4087 c->par = (Evas_Object_Textblock_Paragraph *)
4088 EINA_INLIST_GET(c->par)->next;
4090 /* Update the format stack according to the node's
4092 fnode = n->format_node;
4093 while (fnode && (fnode->text_node == n))
4095 /* Only do this if this actually changes format */
4096 if (fnode->format_change)
4097 _layout_do_format(obj, c, &c->fmt, fnode,
4098 style_pad_l, style_pad_r,
4099 style_pad_t, style_pad_b, EINA_FALSE);
4100 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
4107 /* If it's a new paragraph, just add it. */
4108 _layout_paragraph_new(c, n, EINA_FALSE);
4112 _layout_update_bidi_props(c->o, c->par);
4115 /* For each text node to thorugh all of it's format nodes
4116 * append text from the start to the offset of the next format
4117 * using the last format got. if needed it also creates format
4118 * items this is the core algorithm of the layout mechanism.
4119 * Skip the unicode replacement chars when there are because
4120 * we don't want to print them. */
4121 fnode = n->format_node;
4123 while (fnode && (fnode->text_node == n))
4125 off += fnode->offset;
4126 /* No need to skip on the first run, or a non-visible one */
4127 _layout_text_append(c, c->fmt, n, start, off, o->repch);
4128 _layout_do_format(obj, c, &c->fmt, fnode, style_pad_l,
4129 style_pad_r, style_pad_t, style_pad_b, EINA_TRUE);
4130 if ((c->have_underline2) || (c->have_underline))
4132 if (*style_pad_b < c->underline_extend)
4133 *style_pad_b = c->underline_extend;
4134 c->have_underline = 0;
4135 c->have_underline2 = 0;
4136 c->underline_extend = 0;
4148 fnode->is_new = EINA_FALSE;
4149 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
4151 _layout_text_append(c, c->fmt, n, start, -1, o->repch);
4153 /* Clear the bidi props because we don't need them anymore. */
4154 if (c->par->bidi_props)
4156 evas_bidi_paragraph_props_unref(c->par->bidi_props);
4157 c->par->bidi_props = NULL;
4160 c->par = (Evas_Object_Textblock_Paragraph *)
4161 EINA_INLIST_GET(c->par)->next;
4164 /* Delete the rest of the layout paragraphs */
4167 Evas_Object_Textblock_Paragraph *tmp_par =
4168 (Evas_Object_Textblock_Paragraph *)
4169 EINA_INLIST_GET(c->par)->next;
4171 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4172 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
4173 EINA_INLIST_GET(c->par));
4174 _paragraph_free(obj, c->par);
4178 o->paragraphs = c->paragraphs;
4186 * Create the layout from the nodes.
4188 * @param obj the evas object - NOT NULL.
4189 * @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.
4190 * @param w the object's w, -1 means no wrapping (i.e infinite size)
4191 * @param h the object's h, -1 means inifinte size.
4192 * @param w_ret the object's calculated w.
4193 * @param h_ret the object's calculated h.
4196 _layout(const Evas_Object *obj, int w, int h, int *w_ret, int *h_ret)
4198 Evas_Object_Textblock *o;
4200 int style_pad_l = 0, style_pad_r = 0, style_pad_t = 0, style_pad_b = 0;
4203 o = (Evas_Object_Textblock *)(obj->object_data);
4205 c->obj = (Evas_Object *)obj;
4207 c->paragraphs = c->par = NULL;
4208 c->format_stack = NULL;
4213 c->wmax = c->hmax = 0;
4214 c->maxascent = c->maxdescent = 0;
4215 c->marginl = c->marginr = 0;
4216 c->have_underline = 0;
4217 c->have_underline2 = 0;
4218 c->underline_extend = 0;
4221 c->align_auto = EINA_TRUE;
4223 c->width_changed = (obj->cur.geometry.w != o->last_w);
4225 /* Start of logical layout creation */
4226 /* setup default base style */
4228 Eina_Bool finalize = EINA_FALSE;
4229 if ((c->o->style) && (c->o->style->default_tag))
4231 c->fmt = _layout_format_push(c, NULL, NULL);
4232 _format_fill(c->obj, c->fmt, c->o->style->default_tag);
4233 finalize = EINA_TRUE;
4236 if ((c->o->style_user) && (c->o->style_user->default_tag))
4240 c->fmt = _layout_format_push(c, NULL, NULL);
4242 _format_fill(c->obj, c->fmt, c->o->style_user->default_tag);
4243 finalize = EINA_TRUE;
4247 _format_finalize(c->obj, c->fmt);
4251 if (w_ret) *w_ret = 0;
4252 if (h_ret) *h_ret = 0;
4256 _layout_pre(c, &style_pad_l, &style_pad_r, &style_pad_t, &style_pad_b);
4257 c->paragraphs = o->paragraphs;
4259 /* If there are no paragraphs, create the minimum needed,
4260 * if the last paragraph has no lines/text, create that as well */
4263 _layout_paragraph_new(c, NULL, EINA_TRUE);
4264 o->paragraphs = c->paragraphs;
4266 c->par = (Evas_Object_Textblock_Paragraph *)
4267 EINA_INLIST_GET(c->paragraphs)->last;
4268 if (!c->par->logical_items)
4270 Evas_Object_Textblock_Text_Item *ti;
4271 ti = _layout_text_item_new(c, c->fmt);
4272 ti->parent.text_node = c->par->text_node;
4273 ti->parent.text_pos = 0;
4274 _layout_text_add_logical_item(c, ti, NULL);
4277 /* End of logical layout creation */
4279 /* Start of visual layout creation */
4281 Evas_Object_Textblock_Paragraph *last_vis_par = NULL;
4282 int par_index_step = o->num_paragraphs / TEXTBLOCK_PAR_INDEX_SIZE;
4283 int par_count = 1; /* Force it to take the first one */
4284 int par_index_pos = 0;
4286 if (par_index_step == 0) par_index_step = 1;
4288 /* Clear all of the index */
4289 memset(o->par_index, 0, sizeof(o->par_index));
4291 EINA_INLIST_FOREACH(c->paragraphs, c->par)
4293 _layout_update_par(c);
4295 /* Break if we should stop here. */
4298 last_vis_par = c->par;
4302 if ((par_index_pos < TEXTBLOCK_PAR_INDEX_SIZE) && (--par_count == 0))
4304 par_count = par_index_step;
4306 o->par_index[par_index_pos++] = c->par;
4310 /* Mark all the rest of the paragraphs as invisible */
4313 c->par = (Evas_Object_Textblock_Paragraph *)
4314 EINA_INLIST_GET(c->par)->next;
4317 c->par->visible = 0;
4318 c->par = (Evas_Object_Textblock_Paragraph *)
4319 EINA_INLIST_GET(c->par)->next;
4323 /* Get the last visible paragraph in the layout */
4324 if (!last_vis_par && c->paragraphs)
4325 last_vis_par = (Evas_Object_Textblock_Paragraph *)
4326 EINA_INLIST_GET(c->paragraphs)->last;
4329 c->hmax = last_vis_par->y + last_vis_par->h;
4332 /* Clean the rest of the format stack */
4333 while (c->format_stack)
4335 c->fmt = c->format_stack->data;
4336 c->format_stack = eina_list_remove_list(c->format_stack, c->format_stack);
4337 _format_unref_free(c->obj, c->fmt);
4340 if (w_ret) *w_ret = c->wmax;
4341 if (h_ret) *h_ret = c->hmax;
4343 /* Vertically align the textblock */
4344 if ((o->valign > 0.0) && (c->h > c->hmax))
4346 Evas_Coord adjustment = (c->h - c->hmax) * o->valign;
4347 Evas_Object_Textblock_Paragraph *par;
4348 EINA_INLIST_FOREACH(c->paragraphs, par)
4350 par->y += adjustment;
4354 if ((o->style_pad.l != style_pad_l) || (o->style_pad.r != style_pad_r) ||
4355 (o->style_pad.t != style_pad_t) || (o->style_pad.b != style_pad_b))
4357 o->style_pad.l = style_pad_l;
4358 o->style_pad.r = style_pad_r;
4359 o->style_pad.t = style_pad_t;
4360 o->style_pad.b = style_pad_b;
4361 _paragraphs_clear(obj, c->paragraphs);
4362 _layout(obj, w, h, w_ret, h_ret);
4368 * Relayout the object according to current object size.
4370 * @param obj the evas object - NOT NULL.
4373 _relayout(const Evas_Object *obj)
4375 Evas_Object_Textblock *o;
4377 o = (Evas_Object_Textblock *)(obj->object_data);
4378 _layout(obj, obj->cur.geometry.w, obj->cur.geometry.h,
4379 &o->formatted.w, &o->formatted.h);
4380 o->formatted.valid = 1;
4381 o->last_w = obj->cur.geometry.w;
4382 o->last_h = obj->cur.geometry.h;
4384 o->content_changed = 0;
4385 o->format_changed = EINA_FALSE;
4391 * Find the layout item and line that match the text node and position passed.
4393 * @param obj the evas object - NOT NULL.
4394 * @param n the text node - Not null.
4395 * @param pos the position to look for - valid.
4396 * @param[out] lnr the line found - not null.
4397 * @param[out] tir the item found - not null.
4398 * @see _find_layout_format_item_line_match()
4401 _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)
4403 Evas_Object_Textblock_Paragraph *found_par;
4404 Evas_Object_Textblock_Line *ln;
4405 Evas_Object_Textblock *o;
4407 o = (Evas_Object_Textblock *)(obj->object_data);
4408 if (!o->formatted.valid) _relayout(obj);
4413 _layout_paragraph_render(o, found_par);
4414 EINA_INLIST_FOREACH(found_par->lines, ln)
4416 Evas_Object_Textblock_Item *it;
4418 EINA_INLIST_FOREACH(ln->items, it)
4420 /* FIXME: p should be size_t, same goes for pos */
4421 int p = (int) it->text_pos;
4423 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
4425 Evas_Object_Textblock_Text_Item *ti =
4428 p += (int) ti->text_props.text_len;
4435 if (((pos >= (int) it->text_pos) && (pos < p)))
4453 * Return the line number 'line'.
4455 * @param obj the evas object - NOT NULL.
4456 * @param line the line to find
4457 * @return the line of line number or NULL if no line found.
4459 static Evas_Object_Textblock_Line *
4460 _find_layout_line_num(const Evas_Object *obj, int line)
4462 Evas_Object_Textblock_Paragraph *par;
4463 Evas_Object_Textblock_Line *ln;
4464 Evas_Object_Textblock *o;
4466 o = (Evas_Object_Textblock *)(obj->object_data);
4468 par = _layout_find_paragraph_by_line_no(o, line);
4471 _layout_paragraph_render(o, par);
4472 EINA_INLIST_FOREACH(par->lines, ln)
4474 if (par->line_no + ln->line_no == line) return ln;
4481 evas_object_textblock_add(Evas *e)
4485 MAGIC_CHECK(e, Evas, MAGIC_EVAS);
4488 obj = evas_object_new(e);
4489 evas_object_textblock_init(obj);
4490 evas_object_inject(obj, e);
4494 EAPI Evas_Textblock_Style *
4495 evas_textblock_style_new(void)
4497 Evas_Textblock_Style *ts;
4499 ts = calloc(1, sizeof(Evas_Textblock_Style));
4504 evas_textblock_style_free(Evas_Textblock_Style *ts)
4517 evas_textblock_style_set(Evas_Textblock_Style *ts, const char *text)
4523 /* If the style wasn't really changed, abort. */
4524 if ((!ts->style_text && !text) ||
4525 (ts->style_text && text && !strcmp(text, ts->style_text)))
4528 EINA_LIST_FOREACH(ts->objects, l, obj)
4530 Evas_Object_Textblock *o;
4532 o = (Evas_Object_Textblock *)(obj->object_data);
4533 _evas_textblock_invalidate_all(o);
4534 _evas_textblock_changed(o, obj);
4537 _style_replace(ts, text);
4541 // format MUST be KEY='VALUE'[KEY='VALUE']...
4543 const char *key_start, *key_stop, *val_start;
4545 key_start = key_stop = val_start = NULL;
4551 if (!isspace((unsigned char)(*p)))
4556 if ((*p == '=') || (isspace((unsigned char)(*p))))
4559 else if (!val_start)
4561 if (((*p) == '\'') && (*(p + 1)))
4566 if ((key_start) && (key_stop) && (val_start))
4568 char *tags, *replaces = NULL;
4569 Evas_Object_Style_Tag *tag;
4570 const char *val_stop = NULL;
4575 Eina_Strbuf *buf = eina_strbuf_new();
4576 val_stop = val_start;
4581 /* Break if we found the tag end */
4584 eina_strbuf_append_length(buf, val_stop,
4590 eina_strbuf_append_length(buf, val_stop,
4592 eina_strbuf_append_char(buf, '\'');
4598 replaces = eina_strbuf_string_steal(buf);
4599 eina_strbuf_free(buf);
4601 /* If we didn't find an end, just aboart. */
4604 if (replaces) free(replaces);
4608 tag_len = key_stop - key_start;
4609 replace_len = val_stop - val_start;
4611 tags = malloc(tag_len + 1);
4614 memcpy(tags, key_start, tag_len);
4618 if ((tags) && (replaces))
4620 if (!strcmp(tags, "DEFAULT"))
4622 ts->default_tag = replaces;
4627 tag = calloc(1, sizeof(Evas_Object_Style_Tag));
4630 tag->tag.tag = tags;
4631 tag->tag.replace = replaces;
4632 tag->tag.tag_len = tag_len;
4633 tag->tag.replace_len = replace_len;
4634 ts->tags = (Evas_Object_Style_Tag *)eina_inlist_append(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
4645 if (tags) free(tags);
4646 if (replaces) free(replaces);
4648 key_start = key_stop = val_start = NULL;
4656 evas_textblock_style_get(const Evas_Textblock_Style *ts)
4658 if (!ts) return NULL;
4659 return ts->style_text;
4662 /* textblock styles */
4665 _textblock_style_generic_set(Evas_Object *obj, Evas_Textblock_Style *ts,
4666 Evas_Textblock_Style **obj_ts)
4669 if (ts == *obj_ts) return;
4670 if ((ts) && (ts->delete_me)) return;
4673 Evas_Textblock_Style *old_ts;
4676 free(o->markup_text);
4677 o->markup_text = NULL;
4681 old_ts->objects = eina_list_remove(old_ts->objects, obj);
4682 if ((old_ts->delete_me) && (!old_ts->objects))
4683 evas_textblock_style_free(old_ts);
4687 ts->objects = eina_list_append(ts->objects, obj);
4691 _evas_textblock_invalidate_all(o);
4692 _evas_textblock_changed(o, obj);
4696 evas_object_textblock_style_set(Evas_Object *obj, Evas_Textblock_Style *ts)
4699 _textblock_style_generic_set(obj, ts, &(o->style));
4702 EAPI const Evas_Textblock_Style *
4703 evas_object_textblock_style_get(const Evas_Object *obj)
4705 TB_HEAD_RETURN(NULL);
4710 evas_object_textblock_style_user_push(Evas_Object *obj, Evas_Textblock_Style *ts)
4713 _textblock_style_generic_set(obj, ts, &(o->style_user));
4716 EAPI const Evas_Textblock_Style *
4717 evas_object_textblock_style_user_peek(const Evas_Object *obj)
4719 TB_HEAD_RETURN(NULL);
4720 return o->style_user;
4724 evas_object_textblock_style_user_pop(Evas_Object *obj)
4727 _textblock_style_generic_set(obj, NULL, &(o->style_user));
4731 evas_object_textblock_replace_char_set(Evas_Object *obj, const char *ch)
4734 if (o->repch) eina_stringshare_del(o->repch);
4735 if (ch) o->repch = eina_stringshare_add(ch);
4736 else o->repch = NULL;
4737 _evas_textblock_invalidate_all(o);
4738 _evas_textblock_changed(o, obj);
4742 evas_object_textblock_legacy_newline_set(Evas_Object *obj, Eina_Bool mode)
4745 if (o->legacy_newline == mode)
4748 o->legacy_newline = mode;
4749 /* FIXME: Should recreate all the textnodes... For now, it's just
4750 * for new text inserted. */
4754 evas_object_textblock_legacy_newline_get(const Evas_Object *obj)
4756 TB_HEAD_RETURN(EINA_FALSE);
4757 return o->legacy_newline;
4761 evas_object_textblock_valign_set(Evas_Object *obj, double align)
4764 if (align < 0.0) align = 0.0;
4765 else if (align > 1.0) align = 1.0;
4766 if (o->valign == align) return;
4768 _evas_textblock_changed(o, obj);
4772 evas_object_textblock_valign_get(const Evas_Object *obj)
4774 TB_HEAD_RETURN(0.0);
4779 evas_object_textblock_bidi_delimiters_set(Evas_Object *obj, const char *delim)
4782 eina_stringshare_replace(&o->bidi_delimiters, delim);
4786 evas_object_textblock_bidi_delimiters_get(const Evas_Object *obj)
4788 TB_HEAD_RETURN(NULL);
4789 return o->bidi_delimiters;
4793 evas_object_textblock_replace_char_get(Evas_Object *obj)
4795 TB_HEAD_RETURN(NULL);
4801 * Advance p_buff to point after the end of the string. It's used with the
4802 * @ref escaped_strings[] variable.
4804 * @param p_buff the pointer to the current string.
4807 _escaped_advance_after_end_of_string(const char **p_buf)
4809 while (**p_buf != 0) (*p_buf)++;
4815 * Advance p_buff to point after the end of the string. It's used with the
4816 * @ref escaped_strings[] variable. Also chec if matches.
4819 * @param p_buff the pointer to the current string.
4822 _escaped_is_eq_and_advance(const char *s, const char *s_end,
4823 const char **p_m, const char *m_end)
4825 Eina_Bool reached_end;
4826 for (;((s < s_end) && (*p_m < m_end)); s++, (*p_m)++)
4830 _escaped_advance_after_end_of_string(p_m);
4835 reached_end = !**p_m;
4837 _escaped_advance_after_end_of_string(p_m);
4839 return ((s == s_end) && reached_end);
4845 * @param s the string to match
4847 static inline const char *
4848 _escaped_char_match(const char *s, int *adv)
4850 const char *map_itr, *map_end, *mc, *sc;
4852 map_itr = escape_strings;
4853 map_end = map_itr + sizeof(escape_strings);
4855 while (map_itr < map_end)
4861 _escaped_advance_after_end_of_string(&map_itr);
4862 if (map_itr >= map_end) break;
4867 while ((*mc) && (*sc))
4869 if ((unsigned char)*sc < (unsigned char)*mc) return NULL;
4880 *adv = mc - map_itr;
4883 _escaped_advance_after_end_of_string(&map_itr);
4892 * @param s the string to match
4894 static inline const char *
4895 _escaped_char_get(const char *s, const char *s_end)
4897 /* Handle numeric escape codes. */
4900 static char utf8_escape[7]; /* Support up to 6 bytes utf8 */
4902 Eina_Unicode uchar[2] = { 0, 0 };
4906 s += 2; /* Skip "&#" */
4908 if (tolower((unsigned char)(*s)) == 'x')
4915 if (len >= sizeof(ustr) + 1)
4918 memcpy(ustr, s, len);
4920 uchar[0] = strtol(ustr, NULL, base);
4925 utf8_char = eina_unicode_unicode_to_utf8(uchar, NULL);
4926 strcpy(utf8_escape, utf8_char);
4933 const char *map_itr, *map_end;
4935 map_itr = escape_strings;
4936 map_end = map_itr + sizeof(escape_strings);
4938 while (map_itr < map_end)
4940 if (_escaped_is_eq_and_advance(s, s_end, &map_itr, map_end))
4942 if (map_itr < map_end)
4943 _escaped_advance_after_end_of_string(&map_itr);
4951 evas_textblock_escape_string_get(const char *escape)
4954 if (!escape) return NULL;
4955 return _escaped_char_get(escape, escape + strlen(escape));
4959 evas_textblock_escape_string_range_get(const char *escape_start, const char *escape_end)
4961 if ((!escape_start) || (!escape_end)) return NULL;
4962 return _escaped_char_get(escape_start, escape_end);
4966 evas_textblock_string_escape_get(const char *string, int *len_ret)
4968 if ((!string) || (!len_ret)) return NULL;
4970 return _escaped_char_match(string, len_ret);
4975 * Appends the escaped char beteewn s and s_end to the curosr
4978 * @param s the start of the string
4979 * @param s_end the end of the string.
4982 _append_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
4987 escape = _escaped_char_get(s, s_end);
4989 evas_textblock_cursor_text_append(cur, escape);
4994 * prepends the escaped char beteewn s and s_end to the curosr
4997 * @param s the start of the string
4998 * @param s_end the end of the string.
5001 _prepend_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
5006 escape = _escaped_char_get(s, s_end);
5008 evas_textblock_cursor_text_prepend(cur, escape);
5013 evas_object_textblock_text_markup_set(Evas_Object *obj, const char *text)
5016 if ((text != o->markup_text) && (o->markup_text))
5018 free(o->markup_text);
5019 o->markup_text = NULL;
5022 if (!o->style && !o->style_user)
5024 if (text != o->markup_text)
5026 if (text) o->markup_text = strdup(text);
5031 evas_textblock_cursor_paragraph_first(o->cursor);
5032 evas_textblock_cursor_text_append(o->cursor, "");
5034 evas_object_textblock_text_markup_prepend(o->cursor, text);
5035 /* Point all the cursors to the starrt */
5038 Evas_Textblock_Cursor *data;
5040 evas_textblock_cursor_paragraph_first(o->cursor);
5041 EINA_LIST_FOREACH(o->cursors, l, data)
5042 evas_textblock_cursor_paragraph_first(data);
5047 evas_object_textblock_text_markup_prepend(Evas_Textblock_Cursor *cur, const char *text)
5050 Evas_Object *obj = cur->obj;
5055 char *tag_start, *tag_end, *esc_start, *esc_end;
5057 tag_start = tag_end = esc_start = esc_end = NULL;
5060 /* This loop goes through all of the mark up text until it finds format
5061 * tags, escape sequences or the terminating NULL. When it finds either
5062 * of those, it appends the text found up until that point to the textblock
5063 * proccesses whatever found. It repeats itself until the termainating
5064 * NULL is reached. */
5068 /* If we got to the end of string or just finished/started tag
5069 * or escape sequence handling. */
5071 (tag_end) || (esc_end) ||
5072 (tag_start) || (esc_start))
5076 /* If we reached to a tag ending, analyze the tag */
5078 size_t ttag_len = tag_end - tag_start;
5081 ttag = malloc(ttag_len + 1);
5084 memcpy(ttag, tag_start, ttag_len);
5086 evas_textblock_cursor_format_prepend(cur, ttag);
5089 tag_start = tag_end = NULL;
5093 _prepend_escaped_char(cur, esc_start, esc_end + 1);
5094 esc_start = esc_end = NULL;
5098 _prepend_text_run(cur, s, p);
5108 /* Append the text prior to this to the textblock and mark
5109 * the start of the tag */
5112 _prepend_text_run(cur, s, p);
5128 /* Append the text prior to this to the textblock and mark
5129 * the start of the escape sequence */
5132 _prepend_text_run(cur, s, p);
5144 /* Unicode object replcament char */
5145 else if (!strncmp(_REPLACEMENT_CHAR_UTF8, p,
5146 text_len = strlen(_REPLACEMENT_CHAR_UTF8)) ||
5147 !strncmp(_NEWLINE_UTF8, p,
5148 text_len = strlen(_NEWLINE_UTF8)) ||
5149 !strncmp(_TAB_UTF8, p,
5150 text_len = strlen(_TAB_UTF8)) ||
5151 !strncmp(_PARAGRAPH_SEPARATOR_UTF8, p,
5152 text_len = strlen(_PARAGRAPH_SEPARATOR_UTF8)))
5154 /*FIXME: currently just remove them, maybe do something
5155 * fancier in the future, atm it breaks if this char
5157 _prepend_text_run(cur, s, p);
5158 /* it's also advanced later in this loop need +text_len
5161 s = p + 1; /* One after the end of the replacement char */
5166 _evas_textblock_changed(o, obj);
5172 * An helper function to markup get. Appends the format from fnode to the strbugf txt.
5174 * @param o the textblock object.
5175 * @param txt the strbuf to append to.
5176 * @param fnode the format node to process.
5179 _markup_get_format_append(Eina_Strbuf *txt, Evas_Object_Textblock_Node_Format *fnode)
5181 eina_strbuf_append_char(txt, '<');
5185 // FIXME: need to escape
5186 s = fnode->orig_format;
5187 if (!fnode->opener && !fnode->own_closer)
5188 eina_strbuf_append_char(txt, '/');
5189 eina_strbuf_append(txt, s);
5190 if (fnode->own_closer)
5191 eina_strbuf_append_char(txt, '/');
5193 eina_strbuf_append_char(txt, '>');
5198 * An helper function to markup get. Appends the text in text.
5200 * @param txt the strbuf to append to.
5201 * @param text the text to process.
5204 _markup_get_text_append(Eina_Strbuf *txt, const Eina_Unicode *text)
5206 char *p = eina_unicode_unicode_to_utf8(text, NULL);
5213 escape = _escaped_char_match(p, &adv);
5217 eina_strbuf_append(txt, escape);
5221 eina_strbuf_append_char(txt, *p);
5228 evas_object_textblock_text_markup_get(const Evas_Object *obj)
5230 Evas_Object_Textblock_Node_Text *n;
5231 Eina_Strbuf *txt = NULL;
5233 TB_HEAD_RETURN(NULL);
5234 if (o->markup_text) return(o->markup_text);
5235 txt = eina_strbuf_new();
5236 EINA_INLIST_FOREACH(o->text_nodes, n)
5238 Evas_Object_Textblock_Node_Format *fnode;
5239 Eina_Unicode *text_base, *text;
5243 /* For each text node to thorugh all of it's format nodes
5244 * append text from the start to the offset of the next format
5245 * using the last format got. if needed it also creates format items
5246 * this is the core algorithm of the layout mechanism.
5247 * Skip the unicode replacement chars when there are because
5248 * we don't want to print them. */
5249 len = (int) eina_ustrbuf_length_get(n->unicode);
5251 eina_unicode_strndup(eina_ustrbuf_string_get(n->unicode), len);
5252 fnode = n->format_node;
5254 while (fnode && (fnode->text_node == n))
5256 Eina_Unicode tmp_ch;
5257 off += fnode->offset;
5259 if (off > len) break;
5260 /* No need to skip on the first run */
5262 text[off] = 0; /* Null terminate the part of the string */
5263 _markup_get_text_append(txt, text);
5264 _markup_get_format_append(txt, fnode);
5265 text[off] = tmp_ch; /* Restore the char */
5276 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
5278 /* Add the rest, skip replacement */
5279 _markup_get_text_append(txt, text);
5284 o->markup_text = eina_strbuf_string_steal(txt);
5285 eina_strbuf_free(txt);
5286 return o->markup_text;
5290 evas_textblock_text_markup_to_utf8(const Evas_Object *obj, const char *text)
5292 /* FIXME: Redundant and awful, should be merged with markup_prepend */
5295 char *tag_start, *tag_end, *esc_start, *esc_end;
5297 if (!text) return NULL;
5300 tag_start = tag_end = esc_start = esc_end = NULL;
5301 sbuf = eina_strbuf_new();
5304 /* This loop goes through all of the mark up text until it finds format
5305 * tags, escape sequences or the terminating NULL. When it finds either
5306 * of those, it appends the text found up until that point to the textblock
5307 * proccesses whatever found. It repeats itself until the termainating
5308 * NULL is reached. */
5311 /* If we got to the end of string or just finished/started tag
5312 * or escape sequence handling. */
5314 (tag_end) || (esc_end) ||
5315 (tag_start) || (esc_start))
5319 /* If we reached to a tag ending, analyze the tag */
5323 tag_start++; /* Skip the < */
5324 tag_end--; /* Skip the > */
5325 if ((tag_end > tag_start) && (*(tag_end - 1) == '/'))
5327 tag_end --; /* Skip the terminating '/' */
5328 while (*(tag_end - 1) == ' ')
5329 tag_end--; /* skip trailing ' ' */
5332 ttag_len = tag_end - tag_start;
5334 ttag = malloc(ttag_len + 1);
5337 const char *match = NULL;
5339 memcpy(ttag, tag_start, ttag_len);
5345 match = _style_match_tag(
5346 evas_object_textblock_style_get(obj),
5347 ttag, ttag_len, &replace_len);
5350 if (!match) match = ttag;
5352 if (_IS_PARAGRAPH_SEPARATOR_SIMPLE(match))
5353 eina_strbuf_append(sbuf, _PARAGRAPH_SEPARATOR_UTF8);
5354 else if (_IS_LINE_SEPARATOR(match))
5355 eina_strbuf_append(sbuf, _NEWLINE_UTF8);
5356 else if (_IS_TAB(match))
5357 eina_strbuf_append(sbuf, _TAB_UTF8);
5358 else if (!strncmp(match, "item", 4))
5359 eina_strbuf_append(sbuf, _REPLACEMENT_CHAR_UTF8);
5363 tag_start = tag_end = NULL;
5369 escape = _escaped_char_get(esc_start, esc_end + 1);
5370 if (escape) eina_strbuf_append(sbuf, escape);
5371 esc_start = esc_end = NULL;
5375 eina_strbuf_append_length(sbuf, s, p - s);
5385 /* Append the text prior to this to the textblock and
5386 * mark the start of the tag */
5389 eina_strbuf_append_length(sbuf, s, p - s);
5405 /* Append the text prior to this to the textblock and mark
5406 * the start of the escape sequence */
5409 eina_strbuf_append_length(sbuf, s, p - s);
5424 ret = eina_strbuf_string_steal(sbuf);
5425 eina_strbuf_free(sbuf);
5430 evas_textblock_text_utf8_to_markup(const Evas_Object *obj, const char *text)
5434 int ch, pos = 0, pos2 = 0;
5438 if (!text) return NULL;
5440 sbuf = eina_strbuf_new();
5445 pos2 = evas_string_char_next_get(text, pos2, &ch);
5446 if ((ch <= 0) || (pos2 <= 0)) break;
5449 eina_strbuf_append(sbuf, "<br/>");
5450 else if (ch == _TAB)
5451 eina_strbuf_append(sbuf, "<tab/>");
5453 eina_strbuf_append(sbuf, "<");
5455 eina_strbuf_append(sbuf, ">");
5457 eina_strbuf_append(sbuf, "&");
5458 else if (ch == _PARAGRAPH_SEPARATOR)
5459 eina_strbuf_append(sbuf, "<ps/>");
5460 else if (ch == _REPLACEMENT_CHAR)
5461 eina_strbuf_append(sbuf, "");
5464 eina_strbuf_append_length(sbuf, text + pos, pos2 - pos);
5467 str = eina_strbuf_string_steal(sbuf);
5468 eina_strbuf_free(sbuf);
5477 * Merge the current node with the next, no need to remove PS, already
5480 * @param o the text block object.
5481 * @param to merge into to.
5484 _evas_textblock_nodes_merge(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *to)
5486 Evas_Object_Textblock_Node_Format *itr;
5487 Evas_Object_Textblock_Node_Format *pnode;
5488 Evas_Object_Textblock_Node_Text *from;
5489 const Eina_Unicode *text;
5493 from = _NODE_TEXT(EINA_INLIST_GET(to)->next);
5495 to_len = eina_ustrbuf_length_get(to->unicode);
5496 text = eina_ustrbuf_string_get(from->unicode);
5497 len = eina_ustrbuf_length_get(from->unicode);
5498 eina_ustrbuf_append_length(to->unicode, text, len);
5500 itr = from->format_node;
5501 if (itr && (itr->text_node == from))
5503 pnode = _NODE_FORMAT(EINA_INLIST_GET(itr)->prev);
5504 if (pnode && (pnode->text_node == to))
5506 itr->offset += to_len - _evas_textblock_node_format_pos_get(pnode);
5510 itr->offset += to_len;
5514 while (itr && (itr->text_node == from))
5516 itr->text_node = to;
5517 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
5519 if (!to->format_node || (to->format_node->text_node != to))
5521 to->format_node = from->format_node;
5524 /* When it comes to how we handle it, merging is like removing both nodes
5525 * and creating a new one, se we need to do the needed cleanups. */
5527 to->par->text_node = NULL;
5530 to->is_new = EINA_TRUE;
5532 _evas_textblock_cursors_set_node(o, from, to);
5533 _evas_textblock_node_text_remove(o, from);
5538 * Merge the current node with the next, no need to remove PS, already
5541 * @param cur the cursor that points to the current node
5544 _evas_textblock_cursor_nodes_merge(Evas_Textblock_Cursor *cur)
5546 Evas_Object_Textblock_Node_Text *nnode;
5547 Evas_Object_Textblock *o;
5551 len = eina_ustrbuf_length_get(cur->node->unicode);
5553 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5554 nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
5555 _evas_textblock_nodes_merge(o, cur->node);
5556 _evas_textblock_cursors_update_offset(cur, nnode, 0, len);
5557 _evas_textblock_cursors_set_node(o, nnode, cur->node);
5558 if (nnode == o->cursor->node)
5560 o->cursor->node = cur->node;
5561 o->cursor->pos += len;
5567 * Return the format at a specific position.
5569 * @param cur the cursor to the position.
5570 * @return the format node at the specific position or NULL if not found.
5572 static Evas_Object_Textblock_Node_Format *
5573 _evas_textblock_cursor_node_format_at_pos_get(const Evas_Textblock_Cursor *cur)
5575 Evas_Object_Textblock_Node_Format *node;
5576 Evas_Object_Textblock_Node_Format *itr;
5579 TB_NULL_CHECK(cur->node, NULL);
5581 node = cur->node->format_node;
5582 if (!node) return NULL;
5583 /* If there is no exclusive format node to this paragraph return the
5584 * previous's node */
5585 /* Find the main format node */
5586 EINA_INLIST_FOREACH(node, itr)
5588 if (itr->text_node != cur->node)
5592 if ((position + itr->offset) == cur->pos)
5596 position += itr->offset;
5603 * Return the last format node at the position of the format node n.
5605 * @param n a format node at the position.
5606 * @return the last format node at the position of n.
5608 static Evas_Object_Textblock_Node_Format *
5609 _evas_textblock_node_format_last_at_off(const Evas_Object_Textblock_Node_Format *n)
5611 const Evas_Object_Textblock_Node_Format *nnode;
5612 const Evas_Object_Textblock_Node_Text *tnode;
5613 if (!n) return NULL;
5615 tnode = n->text_node;
5619 nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
5621 while (nnode && (nnode->text_node == tnode) && (nnode->offset == 0));
5623 return (Evas_Object_Textblock_Node_Format *) n;
5628 * Returns the visible format at a specific location.
5630 * @param n a format at the specific position.
5631 * @return the format node at the specific position or NULL if not found.
5633 static Evas_Object_Textblock_Node_Format *
5634 _evas_textblock_node_visible_at_pos_get(const Evas_Object_Textblock_Node_Format *n)
5636 const Evas_Object_Textblock_Node_Format *nnode;
5637 if (!n) return NULL;
5638 /* The visible format is the last one, because it inserts a replacement
5639 * char that advances the next formats. */
5645 if (n->visible) return (Evas_Object_Textblock_Node_Format *) n;
5646 nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
5648 while (nnode && (nnode->offset == 0));
5655 * Return the last format that applies to a specific cursor or at the specific
5656 * position the cursor points to. This means either a cursor at or before the
5657 * position of the cursor in the text node is returned or the previous's text
5658 * node's format node.
5660 * @param cur the position to look at.
5661 * @return the format node found.
5663 static Evas_Object_Textblock_Node_Format *
5664 _evas_textblock_cursor_node_format_before_or_at_pos_get(const Evas_Textblock_Cursor *cur)
5666 Evas_Object_Textblock_Node_Format *node, *pitr = NULL;
5667 Evas_Object_Textblock_Node_Format *itr;
5668 size_t position = 0;
5670 TB_NULL_CHECK(cur->node, NULL);
5672 node = cur->node->format_node;
5673 if (!node) return NULL;
5674 /* If there is no exclusive format node to this paragraph return the
5675 * previous's node */
5676 if (node->text_node != cur->node)
5680 else if (node->offset > cur->pos)
5682 return _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5684 /* Find the main format node */
5685 pitr = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5686 EINA_INLIST_FOREACH(node, itr)
5688 if ((itr->text_node != cur->node) ||
5689 ((position + itr->offset) > cur->pos))
5693 else if ((position + itr->offset) == cur->pos)
5698 position += itr->offset;
5705 * Find the layout item and line that match the cursor.
5707 * @param cur the cursor we are currently at. - NOT NULL.
5708 * @param[out] lnr the line found - not null.
5709 * @param[out] itr the item found - not null.
5710 * @return @c EINA_TRUE if we matched the previous format, @c EINA_FALSE
5714 _find_layout_item_match(const Evas_Textblock_Cursor *cur, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Item **itr)
5716 Evas_Textblock_Cursor cur2;
5717 Eina_Bool previous_format = EINA_FALSE;
5719 cur2.obj = cur->obj;
5720 evas_textblock_cursor_copy(cur, &cur2);
5726 if (_evas_textblock_cursor_is_at_the_end(cur) &&
5727 evas_textblock_cursor_format_is_visible_get(&cur2))
5729 _find_layout_item_line_match(cur2.obj, cur2.node, cur2.pos, lnr, itr);
5730 previous_format = EINA_TRUE;
5734 _find_layout_item_line_match(cur->obj, cur->node, cur->pos, lnr, itr);
5736 return previous_format;
5739 EAPI Evas_Textblock_Cursor *
5740 evas_object_textblock_cursor_get(const Evas_Object *obj)
5742 TB_HEAD_RETURN(NULL);
5746 EAPI Evas_Textblock_Cursor *
5747 evas_object_textblock_cursor_new(const Evas_Object *obj)
5749 Evas_Textblock_Cursor *cur;
5751 TB_HEAD_RETURN(NULL);
5752 cur = calloc(1, sizeof(Evas_Textblock_Cursor));
5753 cur->obj = (Evas_Object *) obj;
5754 cur->node = o->text_nodes;
5757 o->cursors = eina_list_append(o->cursors, cur);
5762 evas_textblock_cursor_free(Evas_Textblock_Cursor *cur)
5764 Evas_Object_Textblock *o;
5767 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5768 if (cur == o->cursor) return;
5769 o->cursors = eina_list_remove(o->cursors, cur);
5774 evas_textblock_cursor_is_format(const Evas_Textblock_Cursor *cur)
5776 if (!cur || !cur->node) return EINA_FALSE;
5777 return (_evas_textblock_cursor_node_format_at_pos_get(cur)) ?
5778 EINA_TRUE : EINA_FALSE;
5781 EAPI const Eina_List *
5782 evas_textblock_node_format_list_get(const Evas_Object *obj, const char *anchor)
5784 TB_HEAD_RETURN(NULL);
5785 if (!strcmp(anchor, "a"))
5786 return o->anchors_a;
5787 else if (!strcmp(anchor, "item"))
5788 return o->anchors_item;
5793 EAPI const Evas_Object_Textblock_Node_Format *
5794 evas_textblock_node_format_first_get(const Evas_Object *obj)
5796 TB_HEAD_RETURN(NULL);
5797 return o->format_nodes;
5800 EAPI const Evas_Object_Textblock_Node_Format *
5801 evas_textblock_node_format_last_get(const Evas_Object *obj)
5803 TB_HEAD_RETURN(NULL);
5804 if (o->format_nodes)
5806 return _NODE_FORMAT(EINA_INLIST_GET(o->format_nodes)->last);
5811 EAPI const Evas_Object_Textblock_Node_Format *
5812 evas_textblock_node_format_next_get(const Evas_Object_Textblock_Node_Format *n)
5814 if (!n) return NULL;
5815 return _NODE_FORMAT(EINA_INLIST_GET(n)->next);
5818 EAPI const Evas_Object_Textblock_Node_Format *
5819 evas_textblock_node_format_prev_get(const Evas_Object_Textblock_Node_Format *n)
5821 if (!n) return NULL;
5822 return _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
5826 evas_textblock_node_format_remove_pair(Evas_Object *obj,
5827 Evas_Object_Textblock_Node_Format *n)
5829 Evas_Object_Textblock_Node_Text *tnode1;
5830 Evas_Object_Textblock_Node_Format *fmt, *found_node = NULL;
5831 Eina_List *fstack = NULL;
5840 const char *fstr = fmt->orig_format;
5842 if (fmt->opener && !fmt->own_closer)
5844 fstack = eina_list_prepend(fstack, fmt);
5846 else if (fstr && !fmt->opener)
5849 fstr_len = strlen(fstr);
5850 /* Generic popper, just pop */
5851 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
5853 fstack = eina_list_remove_list(fstack, fstack);
5860 /* Find the matching format and pop it, if the matching format
5861 * is out format, i.e the last one, pop and break. */
5865 Evas_Object_Textblock_Node_Format *fnode;
5866 EINA_LIST_FOREACH(fstack, i, fnode)
5868 if (_FORMAT_IS_CLOSER_OF(
5869 fnode->orig_format, fstr, fstr_len))
5871 /* Last one, this is our item! */
5872 if (!eina_list_next(i))
5877 fstack = eina_list_remove_list(fstack, i);
5884 fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
5886 while (fmt && fstack);
5890 fstack = eina_list_free(fstack);
5894 size_t ind = _evas_textblock_node_format_pos_get(n);
5895 const char *format = n->format;
5896 Evas_Textblock_Cursor cur;
5899 eina_ustrbuf_remove(n->text_node->unicode, ind, ind + 1);
5900 if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
5902 evas_textblock_cursor_at_format_set(&cur, n);
5903 _evas_textblock_cursor_nodes_merge(&cur);
5905 _evas_textblock_cursors_update_offset(&cur, n->text_node, ind, -1);
5907 tnode1 = n->text_node;
5908 _evas_textblock_node_format_remove(o, n, 0);
5909 if (found_node && (found_node != n))
5911 Evas_Object_Textblock_Node_Text *tnode2;
5912 tnode2 = found_node->text_node;
5913 /* found_node can never be visible! (it's the closing format) */
5914 _evas_textblock_node_format_remove(o, found_node, 0);
5916 /* FIXME: Should be unified in the layout, for example, added to a list
5917 * that checks this kind of removals. But until then, this is very fast
5919 /* Mark all the text nodes in between the removed formats as dirty. */
5922 tnode1->dirty = EINA_TRUE;
5923 if (tnode1 == tnode2)
5926 _NODE_TEXT(EINA_INLIST_GET(tnode1)->next);
5930 _evas_textblock_changed(o, obj);
5934 evas_textblock_cursor_paragraph_first(Evas_Textblock_Cursor *cur)
5936 Evas_Object_Textblock *o;
5938 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5939 cur->node = o->text_nodes;
5945 evas_textblock_cursor_paragraph_last(Evas_Textblock_Cursor *cur)
5947 Evas_Object_Textblock *o;
5948 Evas_Object_Textblock_Node_Text *node;
5951 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5952 node = o->text_nodes;
5955 node = _NODE_TEXT(EINA_INLIST_GET(node)->last);
5959 evas_textblock_cursor_paragraph_char_last(cur);
5970 evas_textblock_cursor_paragraph_next(Evas_Textblock_Cursor *cur)
5972 if (!cur) return EINA_FALSE;
5973 TB_NULL_CHECK(cur->node, EINA_FALSE);
5974 /* If there is a current text node, return the next text node (if exists)
5975 * otherwise, just return False. */
5978 Evas_Object_Textblock_Node_Text *nnode;
5979 nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
5992 evas_textblock_cursor_paragraph_prev(Evas_Textblock_Cursor *cur)
5994 Evas_Object_Textblock_Node_Text *node;
5995 if (!cur) return EINA_FALSE;
5996 TB_NULL_CHECK(cur->node, EINA_FALSE);
5997 /* If the current node is a text node, just get the prev if any,
5998 * if it's a format, get the current text node out of the format and return
5999 * the prev text node if any. */
6001 /* If there is a current text node, return the prev text node
6002 * (if exists) otherwise, just return False. */
6005 Evas_Object_Textblock_Node_Text *pnode;
6006 pnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->prev);
6010 evas_textblock_cursor_paragraph_char_last(cur);
6018 evas_textblock_cursor_set_at_format(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *n)
6020 evas_textblock_cursor_at_format_set(cur, n);
6024 evas_textblock_cursor_format_next(Evas_Textblock_Cursor *cur)
6026 Evas_Object_Textblock_Node_Format *node;
6028 if (!cur) return EINA_FALSE;
6029 TB_NULL_CHECK(cur->node, EINA_FALSE);
6030 /* If the current node is a format node, just get the next if any,
6031 * if it's a text, get the current format node out of the text and return
6032 * the next format node if any. */
6033 node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
6034 node = _evas_textblock_node_format_last_at_off(node);
6037 if (cur->node->format_node)
6039 cur->pos = _evas_textblock_node_format_pos_get(node);
6043 /* If there is a current text node, return the next format node (if exists)
6044 * otherwise, just return False. */
6047 Evas_Object_Textblock_Node_Format *nnode;
6048 nnode = _NODE_FORMAT(EINA_INLIST_GET(node)->next);
6051 cur->node = nnode->text_node;
6052 cur->pos = _evas_textblock_node_format_pos_get(nnode);
6061 evas_textblock_cursor_format_prev(Evas_Textblock_Cursor *cur)
6063 const Evas_Object_Textblock_Node_Format *node;
6064 if (!cur) return EINA_FALSE;
6065 TB_NULL_CHECK(cur->node, EINA_FALSE);
6066 node = evas_textblock_cursor_format_get(cur);
6069 node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
6072 cur->node = node->text_node;
6073 cur->pos = _evas_textblock_node_format_pos_get(node);
6078 /* If there is a current text node, return the next text node (if exists)
6079 * otherwise, just return False. */
6082 Evas_Object_Textblock_Node_Format *pnode;
6083 pnode = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
6086 cur->node = pnode->text_node;
6087 cur->pos = _evas_textblock_node_format_pos_get(pnode);
6095 #ifdef HAVE_LINEBREAK
6097 /* BREAK_AFTER: true if we can break after the current char.
6098 * Both macros assume str[i] is not the terminating nul */
6099 #define BREAK_AFTER(i) \
6100 (breaks[i] == WORDBREAK_BREAK)
6104 #define BREAK_AFTER(i) \
6105 ((!text[i + 1]) || \
6106 (_is_white(text[i]) && !_is_white(text[i + 1])) || \
6107 (!_is_white(text[i]) && _is_white(text[i + 1])))
6112 evas_textblock_cursor_word_start(Evas_Textblock_Cursor *cur)
6114 const Eina_Unicode *text;
6116 #ifdef HAVE_LINEBREAK
6120 if (!cur) return EINA_FALSE;
6121 TB_NULL_CHECK(cur->node, EINA_FALSE);
6123 text = eina_ustrbuf_string_get(cur->node->unicode);
6125 #ifdef HAVE_LINEBREAK
6127 const char *lang = ""; /* FIXME: get lang */
6128 size_t len = eina_ustrbuf_length_get(cur->node->unicode);
6129 breaks = malloc(len);
6130 set_wordbreaks_utf32((const utf32_t *) text, len, lang, breaks);
6136 /* Skip the first one. This ensures we don't point to the nul, and also
6137 * we just don't care about it anyway. */
6140 for ( ; i > 0 ; i--)
6144 /* Advance to the current char */
6152 #ifdef HAVE_LINEBREAK
6159 evas_textblock_cursor_word_end(Evas_Textblock_Cursor *cur)
6161 const Eina_Unicode *text;
6163 #ifdef HAVE_LINEBREAK
6167 if (!cur) return EINA_FALSE;
6168 TB_NULL_CHECK(cur->node, EINA_FALSE);
6170 text = eina_ustrbuf_string_get(cur->node->unicode);
6172 #ifdef HAVE_LINEBREAK
6174 const char *lang = ""; /* FIXME: get lang */
6175 size_t len = eina_ustrbuf_length_get(cur->node->unicode);
6176 breaks = malloc(len);
6177 set_wordbreaks_utf32((const utf32_t *) text, len, lang, breaks);
6183 for ( ; text[i] ; i++)
6187 /* This is the one to break after. */
6194 #ifdef HAVE_LINEBREAK
6201 evas_textblock_cursor_char_next(Evas_Textblock_Cursor *cur)
6204 const Eina_Unicode *text;
6206 if (!cur) return EINA_FALSE;
6207 TB_NULL_CHECK(cur->node, EINA_FALSE);
6210 text = eina_ustrbuf_string_get(cur->node->unicode);
6211 if (text[ind]) ind++;
6212 /* Only allow pointing a null if it's the last paragraph.
6213 * because we don't have a PS there. */
6221 if (!evas_textblock_cursor_paragraph_next(cur))
6223 /* If we already were at the end, that means we don't have
6224 * where to go next we should return FALSE */
6225 if (cur->pos == (size_t) ind)
6239 evas_textblock_cursor_char_prev(Evas_Textblock_Cursor *cur)
6241 if (!cur) return EINA_FALSE;
6242 TB_NULL_CHECK(cur->node, EINA_FALSE);
6249 return evas_textblock_cursor_paragraph_prev(cur);
6253 evas_textblock_cursor_paragraph_char_first(Evas_Textblock_Cursor *cur)
6261 evas_textblock_cursor_paragraph_char_last(Evas_Textblock_Cursor *cur)
6266 TB_NULL_CHECK(cur->node);
6267 ind = eina_ustrbuf_length_get(cur->node->unicode);
6268 /* If it's not the last paragraph, go back one, because we want to point
6269 * to the PS, not the NULL */
6270 if (EINA_INLIST_GET(cur->node)->next)
6281 evas_textblock_cursor_line_char_first(Evas_Textblock_Cursor *cur)
6283 Evas_Object_Textblock *o;
6284 Evas_Object_Textblock_Line *ln = NULL;
6285 Evas_Object_Textblock_Item *it = NULL;
6288 TB_NULL_CHECK(cur->node);
6289 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6290 if (!o->formatted.valid) _relayout(cur->obj);
6292 _find_layout_item_match(cur, &ln, &it);
6297 Evas_Object_Textblock_Item *i;
6299 EINA_INLIST_FOREACH(ln->items, i)
6301 if (it->text_pos > i->text_pos)
6309 cur->pos = it->text_pos;
6310 cur->node = it->text_node;
6315 evas_textblock_cursor_line_char_last(Evas_Textblock_Cursor *cur)
6317 Evas_Object_Textblock *o;
6318 Evas_Object_Textblock_Line *ln = NULL;
6319 Evas_Object_Textblock_Item *it = NULL;
6322 TB_NULL_CHECK(cur->node);
6323 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6324 if (!o->formatted.valid) _relayout(cur->obj);
6326 _find_layout_item_match(cur, &ln, &it);
6331 Evas_Object_Textblock_Item *i;
6333 EINA_INLIST_FOREACH(ln->items, i)
6335 if (it->text_pos < i->text_pos)
6345 cur->node = it->text_node;
6346 cur->pos = it->text_pos;
6347 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
6349 ind = _ITEM_TEXT(it)->text_props.text_len - 1;
6350 if (!IS_AT_END(_ITEM_TEXT(it), ind)) ind++;
6353 else if (!EINA_INLIST_GET(ln)->next && !EINA_INLIST_GET(ln->par)->next)
6362 * checks if a format (as a string) is visible/changes format and sets the
6363 * fnode properties accordingly.
6365 * @param fnode the format node
6366 * @param s the string.
6369 _evas_textblock_format_is_visible(Evas_Object_Textblock_Node_Format *fnode,
6373 Eina_Bool is_opener = EINA_TRUE;
6375 fnode->visible = fnode->format_change = EINA_FALSE;
6376 fnode->anchor = ANCHOR_NONE;
6379 if (!fnode->own_closer)
6381 is_opener = fnode->opener;
6382 fnode->format_change = EINA_TRUE;
6385 while ((item = _format_parse(&s)))
6387 int itlen = s - item;
6388 /* We care about all of the formats even after a - except for
6389 * item which we don't care after a - because it's just a standard
6391 if ((!strncmp(item, "\n", itlen) || !strncmp(item, "\\n", itlen)) ||
6392 (!strncmp(item, "\t", itlen) || !strncmp(item, "\\t", itlen)) ||
6393 (!strncmp(item, "br", itlen) && (itlen >= 2)) ||
6394 (!strncmp(item, "tab", itlen) && (itlen >= 3)) ||
6395 (!strncmp(item, "ps", itlen) && (itlen >= 2)) ||
6396 (!strncmp(item, "item", itlen) && (itlen >= 4) && is_opener))
6398 fnode->visible = EINA_TRUE;
6401 if (is_opener && !strncmp(item, "a", itlen))
6403 fnode->anchor = ANCHOR_A;
6405 else if (is_opener && !strncmp(item, "item", itlen) && (itlen >= 4))
6407 fnode->anchor = ANCHOR_ITEM;
6413 * Sets the cursor to the position of where the fmt points to.
6415 * @param cur the cursor to update.
6416 * @param fmt the format to set according to.
6419 static void __UNUSED__
6420 _evas_textblock_cursor_node_text_at_format(Evas_Textblock_Cursor *cur, Evas_Object_Textblock_Node_Format *fmt)
6422 Evas_Object_Textblock_Node_Text *text;
6423 Evas_Object_Textblock_Node_Format *base_format;
6424 Evas_Object_Textblock_Node_Format *itr;
6425 size_t position = 0;
6427 if (!cur || !fmt) return;
6428 /* Find the main format node */
6429 text = fmt->text_node;
6431 base_format = text->format_node;
6432 EINA_INLIST_FOREACH(base_format, itr)
6438 position += itr->offset;
6440 cur->pos = position;
6447 * Remove pairs of + and - formats and also remove formats without + or -
6448 * i.e formats that pair to themselves. Only removes invisible formats
6449 * that pair themselves, if you want to remove invisible formats that pair
6450 * themselves, please first change fmt->visible to EINA_FALSE.
6452 * @param o the textblock object.
6453 * @param fmt the current format.
6456 _evas_textblock_node_format_remove_matching(Evas_Object_Textblock *o,
6457 Evas_Object_Textblock_Node_Format *fmt)
6459 Evas_Object_Textblock_Node_Text *tnode;
6460 Eina_List *formats = NULL;
6465 tnode = fmt->text_node;
6469 Evas_Object_Textblock_Node_Format *nnode;
6470 const char *fstr = fmt->orig_format;
6472 nnode = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
6475 offset = nnode->offset;
6479 if (fmt->opener && !fmt->own_closer)
6481 formats = eina_list_prepend(formats, fmt);
6483 else if (fstr && !fmt->opener)
6485 Evas_Object_Textblock_Node_Format *fnode;
6487 fstr_len = strlen(fstr);
6488 /* Generic popper, just pop (if there's anything to pop). */
6489 if (formats && (((fstr[0] == ' ') && !fstr[1]) || !fstr[0]))
6491 fnode = eina_list_data_get(formats);
6492 formats = eina_list_remove_list(formats, formats);
6493 _evas_textblock_node_format_remove(o, fnode, 0);
6494 _evas_textblock_node_format_remove(o, fmt, 0);
6496 /* Find the matching format and pop it, if the matching format
6497 * is our format, i.e the last one, pop and break. */
6500 Eina_List *i, *next;
6501 EINA_LIST_FOREACH_SAFE(formats, i, next, fnode)
6503 if (_FORMAT_IS_CLOSER_OF(
6504 fnode->orig_format, fstr, fstr_len))
6506 fnode = eina_list_data_get(i);
6507 formats = eina_list_remove_list(formats, i);
6508 _evas_textblock_node_format_remove(o, fnode, 0);
6509 _evas_textblock_node_format_remove(o, fmt, 0);
6515 else if (!fmt->visible)
6517 _evas_textblock_node_format_remove(o, fmt, 0);
6521 while (fmt && (offset == 0) && (fmt->text_node == tnode));
6522 eina_list_free(formats);
6526 * Add the offset (may be negative) to the first node after fmt which is
6527 * pointing to the text node tnode or to o->format_nodes if fmt is null
6528 * and it points to tnode.
6530 * @param o the textblock object.
6531 * @param tnode the text node the format should point to.
6532 * @param fmt the current format.
6533 * @param offset the offest to add (may be negative).
6536 _evas_textblock_node_format_adjust_offset(Evas_Object_Textblock *o,
6537 Evas_Object_Textblock_Node_Text *tnode,
6538 Evas_Object_Textblock_Node_Format *fmt, int offset)
6542 fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
6546 fmt = o->format_nodes;
6548 if (fmt && (tnode == fmt->text_node))
6550 fmt->offset += offset;
6556 * Removes a format node updating the offset of the next format node and the
6557 * text nodes pointing to this node.
6559 * @param o the textblock object.
6560 * @param n the fromat node to remove
6563 _evas_textblock_node_format_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n, int visible_adjustment)
6565 /* Update the text nodes about the change */
6567 Evas_Object_Textblock_Node_Format *nnode;
6568 nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->next);
6569 /* If there's a next node that belongs to the same text node
6570 * and the curret node was the main one, advance the format node */
6571 if (nnode && (nnode->text_node == n->text_node))
6573 if (nnode->text_node->format_node == n)
6575 nnode->text_node->format_node = nnode;
6580 Evas_Object_Textblock_Node_Text *tnode;
6581 /* If there's no next one update the text nodes */
6582 nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
6583 tnode = n->text_node;
6584 /* Even if it's not the current text_node's main node
6585 * it can still be the next's. */
6586 if (tnode && (tnode->format_node != n))
6588 tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
6590 while (tnode && (tnode->format_node == n))
6592 tnode->format_node = nnode;
6593 tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
6597 _evas_textblock_node_format_adjust_offset(o, n->text_node, n,
6598 n->offset - visible_adjustment);
6600 o->format_nodes = _NODE_FORMAT(eina_inlist_remove(
6601 EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n)));
6602 _evas_textblock_node_format_free(o, n);
6607 * Sets all the offsets of the format nodes between start and end in the text
6608 * node n to 0 and sets visibility to EINA_FALSE.
6609 * If end == -1 end means the end of the string.
6610 * Assumes there is a prev node or the current node will be preserved.
6612 * @param n the text node the positinos refer to.
6613 * @param start the start of where to delete from.
6614 * @param end the end of the section to delete, if end == -1 it means the end of the string.
6615 * @returns @c EINA_TRUE if removed a PS, @c EINA_FALSE otherwise.
6618 _evas_textblock_node_text_adjust_offsets_to_start(Evas_Object_Textblock *o,
6619 Evas_Object_Textblock_Node_Text *n, size_t start, int end)
6621 Evas_Object_Textblock_Node_Format *last_node, *itr;
6622 Evas_Object_Textblock_Node_Text *new_node;
6629 itr = n->format_node;
6630 if (!itr || (itr->text_node != n)) return EINA_FALSE;
6633 if ((end < 0) || ((size_t) end == eina_ustrbuf_length_get(n->unicode)))
6639 /* We don't want the last one */
6643 /* If we are not removing the text node, all should stay in this text
6644 * node, otherwise, everything should move to the previous node */
6645 if ((start == 0) && !use_end)
6647 new_node = _NODE_TEXT(EINA_INLIST_GET(n)->prev);
6658 /* Find the first node after start */
6659 while (itr && (itr->text_node == n))
6666 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6669 if (!itr || (itr->text_node != n))
6674 delta = orig_end - pos;
6675 itr->offset -= pos - start;
6677 while (itr && (itr->text_node == n))
6680 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6684 pos += last_node->offset;
6687 /* start is negative when this gets relevant */
6688 if (use_end && (pos > (size_t) end))
6690 last_node->offset -= delta;
6694 delta = orig_end - pos;
6697 last_node->offset = 0;
6703 last_node->visible = EINA_FALSE;
6705 if (!itr || (itr && (itr->text_node != n)))
6707 /* Remove the PS, and return since it's the end of the node */
6708 if (_IS_PARAGRAPH_SEPARATOR(o, last_node->format))
6710 _evas_textblock_node_format_remove(o, last_node, 0);
6715 last_node->text_node = new_node;
6723 * Returns the first format in the range between start and end in the textblock
6726 * @param o the textblock object.
6727 * @param n the text node the positinos refer to.
6728 * @param start the start of where to delete from.
6729 * @param end the end of the section to delete, if end == -1 it means the end of the string.
6731 static Evas_Object_Textblock_Node_Format *
6732 _evas_textblock_node_text_get_first_format_between(
6733 Evas_Object_Textblock_Node_Text *n, int start, int end)
6735 Evas_Object_Textblock_Node_Format *itr;
6737 itr = n->format_node;
6738 if (end < 0) use_end = 0;
6739 while (itr && (itr->text_node == n))
6741 start -= itr->offset;
6743 if ((end <= 0) && use_end)
6751 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6757 * Removes a text node and the corresponding format nodes.
6759 * @param o the textblock objec.t
6760 * @param n the node to remove.
6763 _evas_textblock_node_text_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *n)
6765 _evas_textblock_node_text_adjust_offsets_to_start(o, n, 0, -1);
6767 o->text_nodes = _NODE_TEXT(eina_inlist_remove(
6768 EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n)));
6769 _evas_textblock_node_text_free(n);
6774 * Return the position where the formats starts at.
6776 * @param fmt the format to return the position of.
6777 * @return the position of the format in the text node it points to.
6780 _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt)
6782 Evas_Object_Textblock_Node_Text *text;
6783 Evas_Object_Textblock_Node_Format *base_format;
6784 Evas_Object_Textblock_Node_Format *itr;
6785 size_t position = 0;
6788 /* Find the main format node */
6789 text = fmt->text_node;
6790 base_format = text->format_node;
6791 EINA_INLIST_FOREACH(base_format, itr)
6797 position += itr->offset;
6799 return position + fmt->offset;
6803 evas_textblock_cursor_pos_get(const Evas_Textblock_Cursor *cur)
6805 Evas_Object_Textblock *o;
6806 Evas_Object_Textblock_Node_Text *n;
6809 if (!cur) return -1;
6810 TB_NULL_CHECK(cur->node, 0);
6811 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6813 while (n != cur->node)
6815 npos += eina_ustrbuf_length_get(n->unicode);
6816 n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
6818 return npos + cur->pos;
6822 evas_textblock_cursor_pos_set(Evas_Textblock_Cursor *cur, int _pos)
6824 Evas_Object_Textblock *o;
6825 Evas_Object_Textblock_Node_Text *n;
6829 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6837 pos = (size_t) _pos;
6841 while (n && (pos >= eina_ustrbuf_length_get(n->unicode)))
6843 pos -= eina_ustrbuf_length_get(n->unicode);
6844 n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
6852 else if (o->text_nodes)
6854 /* In case we went pass the last node, we need to put the cursor
6855 * at the absolute end. */
6856 Evas_Object_Textblock_Node_Text *last_n;
6858 last_n = _NODE_TEXT(EINA_INLIST_GET(o->text_nodes)->last);
6859 pos = eina_ustrbuf_length_get(last_n->unicode);
6868 evas_textblock_cursor_line_set(Evas_Textblock_Cursor *cur, int line)
6870 Evas_Object_Textblock *o;
6871 Evas_Object_Textblock_Line *ln;
6872 Evas_Object_Textblock_Item *it;
6874 if (!cur) return EINA_FALSE;
6875 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6876 if (!o->formatted.valid) _relayout(cur->obj);
6878 ln = _find_layout_line_num(cur->obj, line);
6879 if (!ln) return EINA_FALSE;
6880 it = (Evas_Object_Textblock_Item *)ln->items;
6883 cur->pos = it->text_pos;
6884 cur->node = it->text_node;
6890 cur->node = o->text_nodes;
6896 evas_textblock_cursor_compare(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
6898 Eina_Inlist *l1, *l2;
6900 if (!cur1) return 0;
6901 if (!cur2) return 0;
6902 if (cur1->obj != cur2->obj) return 0;
6903 if ((!cur1->node) || (!cur2->node)) return 0;
6904 if (cur1->node == cur2->node)
6906 if (cur1->pos < cur2->pos) return -1; /* cur1 < cur2 */
6907 else if (cur1->pos > cur2->pos) return 1; /* cur2 < cur1 */
6910 for (l1 = EINA_INLIST_GET(cur1->node),
6911 l2 = EINA_INLIST_GET(cur1->node); (l1) || (l2);)
6913 if (l1 == EINA_INLIST_GET(cur2->node)) return 1; /* cur2 < cur 1 */
6914 else if (l2 == EINA_INLIST_GET(cur2->node)) return -1; /* cur1 < cur 2 */
6915 else if (!l1) return -1; /* cur1 < cur 2 */
6916 else if (!l2) return 1; /* cur2 < cur 1 */
6924 evas_textblock_cursor_copy(const Evas_Textblock_Cursor *cur, Evas_Textblock_Cursor *cur_dest)
6927 if (!cur_dest) return;
6928 if (cur->obj != cur_dest->obj) return;
6929 cur_dest->pos = cur->pos;
6930 cur_dest->node = cur->node;
6938 * Free a text node. Shouldn't be used usually, it's better to use
6939 * @ref _evas_textblock_node_text_remove for most cases .
6941 * @param n the text node to free
6942 * @see _evas_textblock_node_text_remove
6945 _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n)
6948 eina_ustrbuf_free(n->unicode);
6952 n->par->text_node = NULL;
6958 * Create a new text node
6960 * @return the new text node.
6962 static Evas_Object_Textblock_Node_Text *
6963 _evas_textblock_node_text_new(void)
6965 Evas_Object_Textblock_Node_Text *n;
6967 n = calloc(1, sizeof(Evas_Object_Textblock_Node_Text));
6968 n->unicode = eina_ustrbuf_new();
6969 /* We want to layout each paragraph at least once. */
6970 n->dirty = EINA_TRUE;
6971 n->is_new = EINA_TRUE;
6978 * Break a paragraph. This does not add a PS but only splits the paragraph
6979 * where a ps was just added!
6981 * @param cur the cursor to break at.
6982 * @param fnode the format node of the PS just added.
6983 * @return Returns no value.
6986 _evas_textblock_cursor_break_paragraph(Evas_Textblock_Cursor *cur,
6987 Evas_Object_Textblock_Node_Format *fnode)
6989 Evas_Object_Textblock *o;
6990 Evas_Object_Textblock_Node_Text *n;
6993 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6995 n = _evas_textblock_node_text_new();
6996 o->text_nodes = _NODE_TEXT(eina_inlist_append_relative(
6997 EINA_INLIST_GET(o->text_nodes),
6999 EINA_INLIST_GET(cur->node)));
7000 /* Handle text and format changes. */
7003 Evas_Object_Textblock_Node_Format *nnode;
7005 const Eina_Unicode *text;
7007 /* If there was a format node in the delete range,
7008 * make it our format and update the text_node fields,
7009 * otherwise, use the paragraph separator
7010 * of the previous paragraph. */
7011 nnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7012 if (nnode && (nnode->text_node == cur->node))
7014 n->format_node = nnode;
7015 nnode->offset--; /* We don't have to take the replacement char
7016 into account anymore */
7017 while (nnode && (nnode->text_node == cur->node))
7019 nnode->text_node = n;
7020 nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
7025 n->format_node = fnode;
7028 /* cur->pos now points to the PS, move after. */
7029 start = cur->pos + 1;
7030 len = eina_ustrbuf_length_get(cur->node->unicode) - start;
7033 text = eina_ustrbuf_string_get(cur->node->unicode);
7034 eina_ustrbuf_append_length(n->unicode, text + start, len);
7035 eina_ustrbuf_remove(cur->node->unicode, start, start + len);
7036 cur->node->dirty = EINA_TRUE;
7041 fnode = o->format_nodes;
7044 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->last);
7046 n->format_node = fnode;
7052 * Set the node and offset of all the curs after cur.
7054 * @param cur the cursor.
7055 * @param n the current textblock node.
7056 * @param new_node the new node to set.
7059 _evas_textblock_cursors_set_node(Evas_Object_Textblock *o,
7060 const Evas_Object_Textblock_Node_Text *n,
7061 Evas_Object_Textblock_Node_Text *new_node)
7064 Evas_Textblock_Cursor *data;
7066 if (n == o->cursor->node)
7069 o->cursor->node = new_node;
7071 EINA_LIST_FOREACH(o->cursors, l, data)
7073 if (n == data->node)
7076 data->node = new_node;
7083 * Update the offset of all the cursors after cur.
7085 * @param cur the cursor.
7086 * @param n the current textblock node.
7087 * @param start the starting pos.
7088 * @param offset how much to adjust (can be negative).
7091 _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor *cur,
7092 const Evas_Object_Textblock_Node_Text *n,
7093 size_t start, int offset)
7096 Evas_Textblock_Cursor *data;
7097 Evas_Object_Textblock *o;
7098 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7100 if (cur != o->cursor)
7102 if ((n == o->cursor->node) &&
7103 (o->cursor->pos > start))
7105 if ((offset < 0) && (o->cursor->pos <= (size_t) (-1 * offset)))
7111 o->cursor->pos += offset;
7115 EINA_LIST_FOREACH(o->cursors, l, data)
7119 if ((n == data->node) &&
7120 (data->pos > start))
7122 if ((offset < 0) && (data->pos <= (size_t) (-1 * offset)))
7128 data->pos += offset;
7131 else if (!data->node)
7133 data->node = o->text_nodes;
7142 * Mark that the textblock has changed.
7144 * @param o the textblock object.
7145 * @param obj the evas object.
7148 _evas_textblock_changed(Evas_Object_Textblock *o, Evas_Object *obj)
7150 o->formatted.valid = 0;
7151 o->native.valid = 0;
7152 o->content_changed = 1;
7155 free(o->markup_text);
7156 o->markup_text = NULL;
7159 evas_object_change(obj);
7163 _evas_textblock_invalidate_all(Evas_Object_Textblock *o)
7165 Evas_Object_Textblock_Node_Text *n;
7167 EINA_INLIST_FOREACH(o->text_nodes, n)
7169 n->dirty = EINA_TRUE;
7174 evas_textblock_cursor_text_append(Evas_Textblock_Cursor *cur, const char *_text)
7176 Evas_Object_Textblock *o;
7177 Evas_Object_Textblock_Node_Text *n;
7178 Evas_Object_Textblock_Node_Format *fnode = NULL;
7183 text = eina_unicode_utf8_to_unicode(_text, &len);
7184 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7189 Evas_Object_Textblock_Node_Format *nnode;
7190 fnode = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
7191 fnode = _evas_textblock_node_format_last_at_off(fnode);
7192 /* find the node after the current in the same paragraph
7193 * either we find one and then take the next, or we try to get
7194 * the first for the paragraph which must be after our position */
7197 if (!evas_textblock_cursor_format_is_visible_get(cur))
7199 nnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7200 if (nnode && (nnode->text_node == n))
7212 fnode = n->format_node;
7215 else if (o->text_nodes)
7217 n = cur->node = o->text_nodes;
7222 n = _evas_textblock_node_text_new();
7223 o->text_nodes = _NODE_TEXT(eina_inlist_append(
7224 EINA_INLIST_GET(o->text_nodes),
7225 EINA_INLIST_GET(n)));
7229 eina_ustrbuf_insert_length(n->unicode, text, len, cur->pos);
7230 /* Advance the formats */
7231 if (fnode && (fnode->text_node == cur->node))
7232 fnode->offset += len;
7234 /* Update all the cursors after our position. */
7235 _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, len);
7237 _evas_textblock_changed(o, cur->obj);
7238 n->dirty = EINA_TRUE;
7241 if (!o->cursor->node)
7242 o->cursor->node = o->text_nodes;
7247 evas_textblock_cursor_text_prepend(Evas_Textblock_Cursor *cur, const char *_text)
7250 /*append is essentially prepend without advancing */
7251 len = evas_textblock_cursor_text_append(cur, _text);
7252 if (len == 0) return 0;
7253 cur->pos += len; /*Advance */
7259 * Free a format node
7261 * @param o the textblock object
7262 * @param n the format node to free
7265 _evas_textblock_node_format_free(Evas_Object_Textblock *o,
7266 Evas_Object_Textblock_Node_Format *n)
7269 eina_stringshare_del(n->format);
7270 eina_stringshare_del(n->orig_format);
7271 if (n->anchor == ANCHOR_ITEM)
7272 o->anchors_item = eina_list_remove(o->anchors_item, n);
7273 else if (n->anchor == ANCHOR_A)
7274 o->anchors_a = eina_list_remove(o->anchors_a, n);
7280 * Create a new format node.
7282 * @param format the text to create the format node from.
7283 * @param o the textblock object.
7284 * @return Returns the new format node
7286 static Evas_Object_Textblock_Node_Format *
7287 _evas_textblock_node_format_new(Evas_Object_Textblock *o, const char *_format)
7289 Evas_Object_Textblock_Node_Format *n;
7290 const char *format = _format;
7291 const char *pre_stripped_format = NULL;
7293 n = calloc(1, sizeof(Evas_Object_Textblock_Node_Format));
7294 /* Create orig_format and format */
7295 if (format[0] == '<')
7301 format++; /* Advance after '<' */
7302 format_len = strlen(format);
7303 if ((format_len > 0) && format[format_len - 1] == '>')
7305 format_len--; /* We don't care about '>' */
7306 /* Check if it closes itself. Skip the </> case. */
7307 if ((format_len > 1) && format[format_len - 1] == '/')
7309 format_len--; /* We don't care about '/' */
7310 n->own_closer = EINA_TRUE;
7314 if (!o->style_user || !(match = _style_match_tag(o->style_user, format,
7315 format_len, &replace_len)))
7317 match = _style_match_tag(o->style, format, format_len,
7323 if (match[0] != '-')
7325 n->opener = EINA_TRUE;
7326 if (match[0] != '+')
7328 n->own_closer = EINA_TRUE;
7332 pre_stripped_format = match;
7336 if (format[0] == '/')
7343 n->opener = EINA_TRUE;
7347 n->orig_format = eina_stringshare_add_length(format, format_len);
7349 if (!pre_stripped_format)
7350 pre_stripped_format = n->orig_format;
7352 /* Just use as is, it's a special format. */
7355 const char *tmp = format;
7356 if (format[0] != '-')
7358 n->opener = EINA_TRUE;
7359 if (format[0] != '+')
7361 n->own_closer = EINA_TRUE;
7364 if ((*tmp == '+') || (*tmp == '-'))
7367 while (*tmp == ' ') tmp++;
7369 n->orig_format = eina_stringshare_add(tmp);
7370 pre_stripped_format = n->orig_format;
7375 const char *tmp = pre_stripped_format;
7376 if ((*tmp == '+') || (*tmp == '-'))
7379 while (*tmp == ' ') tmp++;
7381 n->format = eina_stringshare_add(tmp);
7385 _evas_textblock_format_is_visible(n, format);
7386 if (n->anchor == ANCHOR_A)
7388 o->anchors_a = eina_list_append(o->anchors_a, n);
7390 else if (n->anchor == ANCHOR_ITEM)
7392 o->anchors_item = eina_list_append(o->anchors_item, n);
7394 n->is_new = EINA_TRUE;
7400 _evas_textblock_cursor_is_at_the_end(const Evas_Textblock_Cursor *cur)
7402 const Eina_Unicode *text;
7404 if (!cur) return EINA_FALSE;
7405 TB_NULL_CHECK(cur->node, EINA_FALSE);
7406 text = eina_ustrbuf_string_get(cur->node->unicode);
7407 if ((cur->pos - 1) > eina_ustrbuf_length_get(cur->node->unicode)) return EINA_FALSE;
7408 return ((text[cur->pos] == 0) && (!EINA_INLIST_GET(cur->node)->next)) ?
7409 EINA_TRUE : EINA_FALSE;
7413 evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, const char *format)
7415 Evas_Object_Textblock *o;
7416 Evas_Object_Textblock_Node_Format *n;
7417 Eina_Bool is_visible;
7419 if (!cur) return EINA_FALSE;
7420 if ((!format) || (format[0] == 0)) return EINA_FALSE;
7421 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7422 /* We should always have at least one text node */
7425 evas_textblock_cursor_text_prepend(cur, "");
7428 n = _evas_textblock_node_format_new(o, format);
7429 is_visible = n->visible;
7433 o->format_nodes = _NODE_FORMAT(eina_inlist_append(
7434 EINA_INLIST_GET(o->format_nodes),
7435 EINA_INLIST_GET(n)));
7437 n->text_node = (EINA_INLIST_GET(n)->prev) ?
7438 _NODE_FORMAT(EINA_INLIST_GET(n)->prev)->text_node :
7440 cur->node = n->text_node;
7444 Evas_Object_Textblock_Node_Format *fmt;
7445 fmt = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
7446 n->text_node = cur->node;
7449 o->format_nodes = _NODE_FORMAT(eina_inlist_prepend(
7450 EINA_INLIST_GET(o->format_nodes),
7451 EINA_INLIST_GET(n)));
7452 n->offset = cur->pos;
7456 if (evas_textblock_cursor_format_is_visible_get(cur))
7458 o->format_nodes = _NODE_FORMAT(eina_inlist_prepend_relative(
7459 EINA_INLIST_GET(o->format_nodes),
7461 EINA_INLIST_GET(fmt)
7463 n->offset = fmt->offset;
7464 if (fmt->text_node->format_node == fmt)
7466 fmt->text_node->format_node = n;
7471 fmt = _evas_textblock_node_format_last_at_off(fmt);
7472 o->format_nodes = _NODE_FORMAT(eina_inlist_append_relative(
7473 EINA_INLIST_GET(o->format_nodes),
7475 EINA_INLIST_GET(fmt)
7477 if (fmt->text_node != cur->node)
7479 n->offset = cur->pos;
7483 n->offset = cur->pos -
7484 _evas_textblock_node_format_pos_get(fmt);
7488 /* Adjust differently if we insert a format char */
7491 _evas_textblock_node_format_adjust_offset(o, cur->node, n,
7496 _evas_textblock_node_format_adjust_offset(o, cur->node, n,
7500 if (!fmt || (fmt->text_node != cur->node))
7502 cur->node->format_node = n;
7505 if (is_visible && cur->node)
7507 Eina_Unicode insert_char;
7508 /* Insert a visual representation according to the type of the
7510 if (_IS_PARAGRAPH_SEPARATOR(o, format))
7511 insert_char = _PARAGRAPH_SEPARATOR;
7512 else if (_IS_LINE_SEPARATOR(format))
7513 insert_char = _NEWLINE;
7514 else if (_IS_TAB(format))
7517 insert_char = _REPLACEMENT_CHAR;
7519 eina_ustrbuf_insert_char(cur->node->unicode, insert_char, cur->pos);
7521 /* Advance all the cursors after our cursor */
7522 _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, 1);
7523 if (_IS_PARAGRAPH_SEPARATOR(o, format))
7525 _evas_textblock_cursor_break_paragraph(cur, n);
7529 /* Handle visible format nodes here */
7530 cur->node->dirty = EINA_TRUE;
7531 n->is_new = EINA_FALSE;
7536 o->format_changed = EINA_TRUE;
7539 _evas_textblock_changed(o, cur->obj);
7541 if (!o->cursor->node)
7542 o->cursor->node = o->text_nodes;
7547 evas_textblock_cursor_format_prepend(Evas_Textblock_Cursor *cur, const char *format)
7549 Eina_Bool is_visible;
7550 /* append is essentially prepend without advancing */
7551 is_visible = evas_textblock_cursor_format_append(cur, format);
7554 /* Advance after the replacement char */
7555 evas_textblock_cursor_char_next(cur);
7563 evas_textblock_cursor_char_delete(Evas_Textblock_Cursor *cur)
7565 Evas_Object_Textblock *o;
7566 Evas_Object_Textblock_Node_Text *n, *n2;
7567 const Eina_Unicode *text;
7570 if (!cur || !cur->node) return;
7571 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7574 text = eina_ustrbuf_string_get(n->unicode);
7581 if (chr == 0) return;
7583 eina_ustrbuf_remove(n->unicode, cur->pos, ind);
7584 /* Remove a format node if needed, and remove the char only if the
7585 * fmt node is not visible */
7587 Eina_Bool should_merge = EINA_FALSE;
7588 Evas_Object_Textblock_Node_Format *fmt, *fmt2;
7589 fmt = _evas_textblock_cursor_node_format_at_pos_get(cur);
7592 const char *format = NULL;
7593 Evas_Object_Textblock_Node_Format *last_fmt;
7594 /* If there's a PS it must be the last become it delimits paragraphs */
7595 last_fmt = _evas_textblock_node_format_last_at_off(fmt);
7596 format = last_fmt->format;
7597 if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
7599 /* If it was a paragraph separator, we should merge the
7600 * current with the next, there must be a next. */
7601 should_merge = EINA_TRUE;
7603 /* If a singnular, mark as invisible, so we'll delete it. */
7604 if (!format || last_fmt->own_closer)
7606 last_fmt->visible = EINA_FALSE;
7610 fmt2 = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
7611 fmt2 = _evas_textblock_node_format_last_at_off(fmt2);
7612 _evas_textblock_node_format_adjust_offset(o, cur->node, fmt2,
7617 _evas_textblock_cursor_nodes_merge(cur);
7620 _evas_textblock_node_format_remove_matching(o, fmt);
7623 if (cur->pos == eina_ustrbuf_length_get(n->unicode))
7625 n2 = _NODE_TEXT(EINA_INLIST_GET(n)->next);
7633 _evas_textblock_cursors_update_offset(cur, n, ppos, -(ind - ppos));
7634 _evas_textblock_changed(o, cur->obj);
7635 cur->node->dirty = EINA_TRUE;
7639 evas_textblock_cursor_range_delete(Evas_Textblock_Cursor *cur1, Evas_Textblock_Cursor *cur2)
7641 Evas_Object_Textblock_Node_Format *fnode = NULL;
7642 Evas_Object_Textblock *o;
7643 Evas_Object_Textblock_Node_Text *n1, *n2;
7644 Eina_Bool should_merge = EINA_FALSE, reset_cursor = EINA_FALSE;
7646 if (!cur1 || !cur1->node) return;
7647 if (!cur2 || !cur2->node) return;
7648 if (cur1->obj != cur2->obj) return;
7649 o = (Evas_Object_Textblock *)(cur1->obj->object_data);
7650 if (evas_textblock_cursor_compare(cur1, cur2) > 0)
7652 Evas_Textblock_Cursor *tc;
7660 if ((evas_textblock_cursor_compare(o->cursor, cur1) >= 0) &&
7661 (evas_textblock_cursor_compare(cur2, o->cursor) >= 0))
7663 reset_cursor = EINA_TRUE;
7669 if ((cur1->pos == 0) &&
7670 (cur2->pos == eina_ustrbuf_length_get(n1->unicode)))
7672 /* Remove the whole node. */
7673 Evas_Object_Textblock_Node_Text *n =
7674 _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7677 should_merge = EINA_TRUE;
7681 n = _NODE_TEXT(EINA_INLIST_GET(n1)->prev);
7685 /* Clear the whole textblock - do it nicer. */
7686 evas_object_textblock_text_markup_set(cur1->obj, "");
7693 should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o,
7694 n1, cur1->pos, cur2->pos);
7696 eina_ustrbuf_remove(n1->unicode, cur1->pos, cur2->pos);
7697 _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos, - (cur2->pos - cur1->pos));
7701 Evas_Object_Textblock_Node_Text *n;
7703 _evas_textblock_node_text_adjust_offsets_to_start(o, n1, cur1->pos, -1);
7704 n = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7705 /* Remove all the text nodes between */
7706 while (n && (n != n2))
7708 Evas_Object_Textblock_Node_Text *nnode;
7710 nnode = _NODE_TEXT(EINA_INLIST_GET(n)->next);
7711 _evas_textblock_node_text_adjust_offsets_to_start(o, n, 0, -1);
7712 _evas_textblock_nodes_merge(o, n1);
7715 should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o, n2,
7718 /* Remove the formats and the strings in the first and last nodes */
7719 len = eina_ustrbuf_length_get(n1->unicode);
7720 eina_ustrbuf_remove(n1->unicode, cur1->pos, len);
7721 eina_ustrbuf_remove(n2->unicode, 0, cur2->pos);
7722 /* Merge the nodes because we removed the PS */
7723 _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos,
7725 _evas_textblock_cursors_update_offset(cur2, cur2->node, 0, -cur2->pos);
7727 _evas_textblock_nodes_merge(o, n1);
7729 fnode = _evas_textblock_cursor_node_format_at_pos_get(cur1);
7733 n1->dirty = n2->dirty = EINA_TRUE;
7737 /* We call this function instead of the cursor one because we already
7738 * updated the cursors */
7739 _evas_textblock_nodes_merge(o, n1);
7741 _evas_textblock_node_format_remove_matching(o, fnode);
7743 evas_textblock_cursor_copy(cur1, cur2);
7745 evas_textblock_cursor_copy(cur1, o->cursor);
7747 _evas_textblock_changed(o, cur1->obj);
7752 evas_textblock_cursor_content_get(const Evas_Textblock_Cursor *cur)
7754 if (!cur || !cur->node) return NULL;
7755 if (evas_textblock_cursor_format_is_visible_get(cur))
7758 Evas_Object_Textblock_Node_Format *fnode;
7760 fnode = _evas_textblock_node_visible_at_pos_get(
7761 evas_textblock_cursor_format_get(cur));
7763 buf = eina_strbuf_new();
7764 _markup_get_format_append(buf, fnode);
7765 ret = eina_strbuf_string_steal(buf);
7766 eina_strbuf_free(buf);
7772 const Eina_Unicode *ustr;
7773 Eina_Unicode buf[2];
7776 ustr = eina_ustrbuf_string_get(cur->node->unicode);
7777 buf[0] = ustr[cur->pos];
7779 s = eina_unicode_unicode_to_utf8(buf, NULL);
7786 _evas_textblock_cursor_range_text_markup_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2)
7788 Evas_Object_Textblock_Node_Text *tnode;
7790 Evas_Textblock_Cursor *cur2;
7791 buf = eina_strbuf_new();
7793 if (!cur1 || !cur1->node) return NULL;
7794 if (!_cur2 || !_cur2->node) return NULL;
7795 if (cur1->obj != _cur2->obj) return NULL;
7796 if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
7798 const Evas_Textblock_Cursor *tc;
7804 /* Work on a local copy of the cur */
7805 cur2 = alloca(sizeof(Evas_Textblock_Cursor));
7806 cur2->obj = _cur2->obj;
7807 evas_textblock_cursor_copy(_cur2, cur2);
7809 /* Parse the text between the cursors. */
7810 for (tnode = cur1->node ; tnode ;
7811 tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next))
7813 Evas_Object_Textblock_Node_Format *fnode;
7814 Eina_Unicode *text_base, *text;
7818 eina_unicode_strndup(eina_ustrbuf_string_get(tnode->unicode),
7819 eina_ustrbuf_length_get(tnode->unicode));
7820 if (tnode == cur2->node)
7822 fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7823 cur1->pos, cur2->pos);
7825 else if (tnode == cur1->node)
7827 fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7832 fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7835 /* Init the offset so the first one will count starting from cur1->pos
7836 * and not the previous format node */
7837 if (tnode == cur1->node)
7841 off = _evas_textblock_node_format_pos_get(fnode) -
7842 cur1->pos - fnode->offset;
7850 while (fnode && (fnode->text_node == tnode))
7852 Eina_Unicode tmp_ch;
7853 off += fnode->offset;
7854 if ((tnode == cur2->node) &&
7855 ((size_t) (text - text_base + off) >= cur2->pos))
7859 /* No need to skip on the first run */
7861 text[off] = 0; /* Null terminate the part of the string */
7862 _markup_get_text_append(buf, text);
7863 _markup_get_format_append(buf, fnode);
7864 text[off] = tmp_ch; /* Restore the char */
7875 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7877 /* If we got to the last node, stop and add the rest outside */
7878 if (cur2->node == tnode)
7880 /* Add the rest, skip replacement */
7881 /* Don't go past the second cursor pos */
7882 text_base[cur2->pos] = '\0';
7883 _markup_get_text_append(buf, text);
7889 /* Add the rest, skip replacement */
7890 _markup_get_text_append(buf, text);
7894 /* return the string */
7897 ret = eina_strbuf_string_steal(buf);
7898 eina_strbuf_free(buf);
7904 _evas_textblock_cursor_range_text_plain_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2)
7907 Evas_Object_Textblock_Node_Text *n1, *n2;
7908 Evas_Textblock_Cursor *cur2;
7910 buf = eina_ustrbuf_new();
7912 if (!cur1 || !cur1->node) return NULL;
7913 if (!_cur2 || !_cur2->node) return NULL;
7914 if (cur1->obj != _cur2->obj) return NULL;
7915 if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
7917 const Evas_Textblock_Cursor *tc;
7925 /* Work on a local copy of the cur */
7926 cur2 = alloca(sizeof(Evas_Textblock_Cursor));
7927 cur2->obj = _cur2->obj;
7928 evas_textblock_cursor_copy(_cur2, cur2);
7933 const Eina_Unicode *tmp;
7934 tmp = eina_ustrbuf_string_get(n1->unicode);
7935 eina_ustrbuf_append_length(buf, tmp + cur1->pos, cur2->pos - cur1->pos);
7939 const Eina_Unicode *tmp;
7940 tmp = eina_ustrbuf_string_get(n1->unicode);
7941 eina_ustrbuf_append(buf, tmp + cur1->pos);
7942 n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7945 tmp = eina_ustrbuf_string_get(n1->unicode);
7946 eina_ustrbuf_append_length(buf, tmp,
7947 eina_ustrbuf_length_get(n1->unicode));
7948 n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7950 tmp = eina_ustrbuf_string_get(n2->unicode);
7951 eina_ustrbuf_append_length(buf, tmp, cur2->pos);
7954 /* Free and return */
7957 ret = eina_unicode_unicode_to_utf8(eina_ustrbuf_string_get(buf), NULL);
7958 eina_ustrbuf_free(buf);
7964 evas_textblock_cursor_range_formats_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
7967 Eina_List *ret = NULL;
7968 Evas_Object_Textblock_Node_Text *n1, *n2;
7969 Evas_Object_Textblock_Node_Format *first, *last;
7970 if (!cur1 || !cur1->node) return NULL;
7971 if (!cur2 || !cur2->node) return NULL;
7972 if (cur1->obj != cur2->obj) return NULL;
7975 TB_HEAD_RETURN(NULL);
7977 if (evas_textblock_cursor_compare(cur1, cur2) > 0)
7979 const Evas_Textblock_Cursor *tc;
7988 /* FIXME: Change first and last getting to format_before_or_at_pos_get */
7990 last = n2->format_node;
7992 /* If n2->format_node is NULL, we don't have formats in the tb/range. */
7995 /* If the found format is on our text node, we should go to the last
7996 * one, otherwise, the one we found is good enough. */
7997 if (last->text_node == n2)
7999 Evas_Object_Textblock_Node_Format *fnode = last;
8000 while (fnode && (fnode->text_node == n2))
8003 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
8007 /* If the first format node is within the range (i.e points to n1) or if
8008 * we have other formats in the range, go through them */
8009 first = n1->format_node;
8010 if ((first->text_node == n1) || (first != last))
8012 Evas_Object_Textblock_Node_Format *fnode = first;
8013 /* Go to the first one in the range */
8014 if (first->text_node != n1)
8016 first = _NODE_FORMAT(EINA_INLIST_GET(first)->next);
8021 ret = eina_list_append(ret, fnode);
8024 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
8033 evas_textblock_cursor_range_text_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2, Evas_Textblock_Text_Type format)
8035 if (format == EVAS_TEXTBLOCK_TEXT_MARKUP)
8036 return _evas_textblock_cursor_range_text_markup_get(cur1, cur2);
8037 else if (format == EVAS_TEXTBLOCK_TEXT_PLAIN)
8038 return _evas_textblock_cursor_range_text_plain_get(cur1, cur2);
8040 return NULL; /* Not yet supported */
8044 evas_textblock_cursor_paragraph_text_get(const Evas_Textblock_Cursor *cur)
8046 Evas_Textblock_Cursor cur1, cur2;
8047 if (!cur) return NULL;
8048 TB_NULL_CHECK(cur->node, NULL);
8049 if (cur->node->utf8)
8051 free(cur->node->utf8);
8053 cur1.obj = cur2.obj = cur->obj;
8054 cur1.node = cur2.node = cur->node;
8055 evas_textblock_cursor_paragraph_char_first(&cur1);
8056 evas_textblock_cursor_paragraph_char_last(&cur2);
8058 cur->node->utf8 = evas_textblock_cursor_range_text_get(&cur1, &cur2,
8059 EVAS_TEXTBLOCK_TEXT_MARKUP);
8060 return cur->node->utf8;
8064 evas_textblock_cursor_paragraph_text_length_get(const Evas_Textblock_Cursor *cur)
8067 if (!cur) return -1;
8068 TB_NULL_CHECK(cur->node, -1);
8069 len = eina_ustrbuf_length_get(cur->node->unicode);
8071 if (EINA_INLIST_GET(cur->node)->next)
8072 return len - 1; /* Remove the paragraph separator */
8077 EAPI const Evas_Object_Textblock_Node_Format *
8078 evas_textblock_cursor_format_get(const Evas_Textblock_Cursor *cur)
8080 if (!cur) return NULL;
8081 TB_NULL_CHECK(cur->node, NULL);
8082 return _evas_textblock_cursor_node_format_at_pos_get(cur);
8086 evas_textblock_node_format_text_get(const Evas_Object_Textblock_Node_Format *fmt)
8088 static char *ret = NULL;
8091 if (!fmt) return NULL;
8094 ret = malloc(strlen(fmt->orig_format) + 2 + 1);
8097 if (fmt->opener && !fmt->own_closer)
8102 else if (!fmt->opener)
8107 strcpy(tmp, fmt->orig_format);
8112 evas_textblock_cursor_at_format_set(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *fmt)
8114 if (!fmt || !cur) return;
8115 cur->node = fmt->text_node;
8116 cur->pos = _evas_textblock_node_format_pos_get(fmt);
8120 evas_textblock_cursor_format_is_visible_get(const Evas_Textblock_Cursor *cur)
8122 const Eina_Unicode *text;
8124 if (!cur) return EINA_FALSE;
8125 TB_NULL_CHECK(cur->node, EINA_FALSE);
8126 if (!evas_textblock_cursor_is_format(cur)) return EINA_FALSE;
8127 text = eina_ustrbuf_string_get(cur->node->unicode);
8128 return EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(text[cur->pos]);
8132 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)
8135 const Evas_Textblock_Cursor *dir_cur;
8136 Evas_Textblock_Cursor cur2;
8137 Evas_Object_Textblock *o;
8138 if (!cur) return -1;
8139 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8140 if (!o->formatted.valid) _relayout(cur->obj);
8143 if (ctype == EVAS_TEXTBLOCK_CURSOR_UNDER)
8145 ret = evas_textblock_cursor_pen_geometry_get(cur, cx, cy, cw, ch);
8147 else if (ctype == EVAS_TEXTBLOCK_CURSOR_BEFORE)
8149 /* In the case of a "before cursor", we should get the coordinates
8150 * of just after the previous char (which in bidi text may not be
8151 * just before the current char). */
8152 Evas_Coord x, y, h, w;
8153 Evas_Object_Textblock_Node_Format *fmt;
8155 /* If it's at the end of the line, we want to get the position, not
8156 * the position of the previous */
8157 if ((cur->pos > 0) && !_evas_textblock_cursor_is_at_the_end(cur))
8160 Eina_Bool before_char = EINA_FALSE;
8162 cur2.obj = cur->obj;
8163 evas_textblock_cursor_copy(cur, &cur2);
8164 evas_textblock_cursor_char_prev(&cur2);
8166 fmt = _evas_textblock_cursor_node_format_at_pos_get(&cur2);
8168 if (!fmt || !_IS_LINE_SEPARATOR(fmt->format))
8172 before_char = EINA_FALSE;
8178 before_char = EINA_TRUE;
8181 ret = evas_textblock_cursor_pen_geometry_get(
8182 dir_cur, &x, &y, &w, &h);
8184 /* Adjust if the char is an rtl char */
8187 Eina_Bool is_rtl = EINA_FALSE;
8188 if (dir_cur->node->par->is_bidi)
8190 Evas_Object_Textblock_Line *ln;
8191 Evas_Object_Textblock_Item *it;
8192 _find_layout_item_match(dir_cur, &ln, &it);
8193 if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8194 (_ITEM_TEXT(it)->text_props.bidi.dir ==
8195 EVAS_BIDI_DIRECTION_RTL))
8197 else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8198 (_ITEM_FORMAT(it)->bidi_dir ==
8199 EVAS_BIDI_DIRECTION_RTL))
8203 if ((!before_char && is_rtl) ||
8204 (before_char && !is_rtl))
8206 /* Just don't advance the width */
8212 else if (cur->pos == 0)
8214 ret = evas_textblock_cursor_pen_geometry_get(
8215 dir_cur, &x, &y, &w, &h);
8217 Eina_Bool is_rtl = EINA_FALSE;
8218 if (dir_cur->node && dir_cur->node->par->is_bidi)
8220 Evas_Object_Textblock_Line *ln;
8221 Evas_Object_Textblock_Item *it;
8222 _find_layout_item_match(dir_cur, &ln, &it);
8223 if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8224 (_ITEM_TEXT(it)->text_props.bidi.dir ==
8225 EVAS_BIDI_DIRECTION_RTL))
8227 else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8228 (_ITEM_FORMAT(it)->bidi_dir ==
8229 EVAS_BIDI_DIRECTION_RTL))
8233 /* Adjust if the char is an rtl char */
8234 if ((ret >= 0) && (!is_rtl))
8236 /* Just don't advance the width */
8243 ret = evas_textblock_cursor_pen_geometry_get(
8244 dir_cur, &x, &y, &w, &h);
8248 if (cx) *cx = x + w;
8255 if (dir && dir_cur && dir_cur->node)
8258 Eina_Bool is_rtl = EINA_FALSE;
8259 if (dir_cur->node->par->is_bidi)
8261 Evas_Object_Textblock_Line *ln;
8262 Evas_Object_Textblock_Item *it;
8263 _find_layout_item_match(dir_cur, &ln, &it);
8264 if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8265 (_ITEM_TEXT(it)->text_props.bidi.dir ==
8266 EVAS_BIDI_DIRECTION_RTL))
8268 else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8269 (_ITEM_FORMAT(it)->bidi_dir ==
8270 EVAS_BIDI_DIRECTION_RTL))
8274 if (_evas_textblock_cursor_is_at_the_end(dir_cur) && (dir_cur->pos > 0))
8277 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
8279 else if (dir_cur->pos > 0)
8282 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
8287 *dir = EVAS_BIDI_DIRECTION_LTR;
8295 * Returns the geometry/pen position (depending on query_func) of the char
8298 * @param cur the position of the char.
8299 * @param query_func the query function to use.
8300 * @param cx the x of the char (or pen_x in the case of pen position).
8301 * @param cy the y of the char.
8302 * @param cw the w of the char (or advance in the case pen position).
8303 * @param ch the h of the char.
8304 * @return line number of the char on success, -1 on error.
8307 _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)
8309 Evas_Object_Textblock *o;
8310 Evas_Object_Textblock_Line *ln = NULL;
8311 Evas_Object_Textblock_Item *it = NULL;
8312 Evas_Object_Textblock_Text_Item *ti = NULL;
8313 Evas_Object_Textblock_Format_Item *fi = NULL;
8314 int x = 0, y = 0, w = 0, h = 0;
8316 Eina_Bool previous_format;
8318 if (!cur) return -1;
8319 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8320 if (!o->formatted.valid) _relayout(cur->obj);
8326 if (!o->paragraphs) return -1;
8327 ln = o->paragraphs->lines;
8329 if (cx) *cx = ln->x;
8330 if (cy) *cy = ln->par->y + ln->y;
8331 if (cw) *cw = ln->w;
8332 if (ch) *ch = ln->h;
8333 return ln->par->line_no + ln->line_no;
8339 previous_format = _find_layout_item_match(cur, &ln, &it);
8344 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8346 ti = _ITEM_TEXT(it);
8350 fi = _ITEM_FORMAT(it);
8355 pos = cur->pos - ti->parent.text_pos;
8357 if (pos < 0) pos = 0;
8358 if (ti->parent.format->font.font)
8360 query_func(cur->ENDT,
8361 ti->parent.format->font.font,
8367 x += ln->x + _ITEM(ti)->x;
8373 y = ln->par->y + ln->y;
8378 if (previous_format)
8380 if (_IS_LINE_SEPARATOR(fi->item))
8383 y = ln->par->y + ln->y + ln->h;
8388 if (ln->par->direction == EVAS_BIDI_DIRECTION_RTL)
8397 y = ln->par->y + ln->y;
8404 x = ln->x + _ITEM(fi)->x;
8405 y = ln->par->y + ln->y;
8418 return ln->par->line_no + ln->line_no;
8422 evas_textblock_cursor_char_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8424 if (!cur) return -1;
8425 return _evas_textblock_cursor_char_pen_geometry_common_get(
8426 cur->ENFN->font_char_coords_get, cur, cx, cy, cw, ch);
8430 evas_textblock_cursor_pen_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8432 if (!cur) return -1;
8433 return _evas_textblock_cursor_char_pen_geometry_common_get(
8434 cur->ENFN->font_pen_coords_get, cur, cx, cy, cw, ch);
8438 evas_textblock_cursor_line_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8440 Evas_Object_Textblock *o;
8441 Evas_Object_Textblock_Line *ln = NULL;
8442 Evas_Object_Textblock_Item *it = NULL;
8445 if (!cur) return -1;
8446 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8447 if (!o->formatted.valid) _relayout(cur->obj);
8450 ln = o->paragraphs->lines;
8454 _find_layout_item_match(cur, &ln, &it);
8458 y = ln->par->y + ln->y;
8465 return ln->par->line_no + ln->line_no;
8469 evas_textblock_cursor_visible_range_get(Evas_Textblock_Cursor *start, Evas_Textblock_Cursor *end)
8473 Evas_Object *obj = start->obj;
8474 TB_HEAD_RETURN(EINA_FALSE);
8475 e = evas_object_evas_get(obj);
8476 cy = 0 - obj->cur.geometry.y;
8478 evas_textblock_cursor_line_coord_set(start, cy);
8479 evas_textblock_cursor_line_coord_set(end, cy + ch);
8480 evas_textblock_cursor_line_char_last(end);
8486 evas_textblock_cursor_char_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord x, Evas_Coord y)
8488 Evas_Object_Textblock *o;
8489 Evas_Object_Textblock_Paragraph *found_par;
8490 Evas_Object_Textblock_Line *ln;
8491 Evas_Object_Textblock_Item *it = NULL;
8493 if (!cur) return EINA_FALSE;
8494 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8495 if (!o->formatted.valid) _relayout(cur->obj);
8496 x += o->style_pad.l;
8497 y += o->style_pad.t;
8499 found_par = _layout_find_paragraph_by_y(o, y);
8502 _layout_paragraph_render(o, found_par);
8503 EINA_INLIST_FOREACH(found_par->lines, ln)
8505 if (ln->par->y + ln->y > y) break;
8506 if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
8508 /* If before or after the line, go to start/end according
8509 * to paragraph direction. */
8512 cur->pos = ln->items->text_pos;
8513 cur->node = found_par->text_node;
8514 if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
8516 evas_textblock_cursor_line_char_last(cur);
8520 evas_textblock_cursor_line_char_first(cur);
8524 else if (x >= ln->x + ln->w)
8526 cur->pos = ln->items->text_pos;
8527 cur->node = found_par->text_node;
8528 if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
8530 evas_textblock_cursor_line_char_first(cur);
8534 evas_textblock_cursor_line_char_last(cur);
8539 EINA_INLIST_FOREACH(ln->items, it)
8541 if (((it->x + ln->x) <= x) && (((it->x + ln->x) + it->adv) > x))
8543 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8547 Evas_Object_Textblock_Text_Item *ti;
8548 ti = _ITEM_TEXT(it);
8551 if (ti->parent.format->font.font)
8552 pos = cur->ENFN->font_char_at_coords_get(
8554 ti->parent.format->font.font,
8556 x - it->x - ln->x, 0,
8557 &cx, &cy, &cw, &ch);
8560 cur->pos = pos + it->text_pos;
8561 cur->node = it->text_node;
8566 Evas_Object_Textblock_Format_Item *fi;
8567 fi = _ITEM_FORMAT(it);
8568 cur->pos = fi->parent.text_pos;
8569 cur->node = found_par->text_node;
8577 else if (o->paragraphs && (y >= o->paragraphs->y + o->formatted.h))
8579 /* If we are after the last paragraph, use the last position in the
8581 evas_textblock_cursor_paragraph_last(cur);
8584 else if (o->paragraphs && (y < o->paragraphs->y))
8586 evas_textblock_cursor_paragraph_first(cur);
8594 evas_textblock_cursor_line_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord y)
8596 Evas_Object_Textblock *o;
8597 Evas_Object_Textblock_Paragraph *found_par;
8598 Evas_Object_Textblock_Line *ln;
8600 if (!cur) return -1;
8601 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8602 if (!o->formatted.valid) _relayout(cur->obj);
8603 y += o->style_pad.t;
8605 found_par = _layout_find_paragraph_by_y(o, y);
8609 _layout_paragraph_render(o, found_par);
8610 EINA_INLIST_FOREACH(found_par->lines, ln)
8612 if (ln->par->y + ln->y > y) break;
8613 if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
8615 evas_textblock_cursor_line_set(cur, ln->par->line_no +
8617 return ln->par->line_no + ln->line_no;
8621 else if (o->paragraphs && (y >= o->paragraphs->y + o->formatted.h))
8624 /* If we are after the last paragraph, use the last position in the
8626 evas_textblock_cursor_paragraph_last(cur);
8627 if (cur->node && cur->node->par)
8629 line_no = cur->node->par->line_no;
8630 if (cur->node->par->lines)
8632 line_no += ((Evas_Object_Textblock_Line *)
8633 EINA_INLIST_GET(cur->node->par->lines)->last)->line_no;
8638 else if (o->paragraphs && (y < o->paragraphs->y))
8641 evas_textblock_cursor_paragraph_first(cur);
8642 if (cur->node && cur->node->par)
8644 line_no = cur->node->par->line_no;
8653 * Updates x and w according to the text direction, position in text and
8654 * if it's a special case switch
8656 * @param ti the text item we are working on
8657 * @param x the current x (we get) and the x we return
8658 * @param w the current w (we get) and the w we return
8659 * @param start if this is the first item or not
8660 * @param switch_items toogles item switching (rtl cases)
8663 _evas_textblock_range_calc_x_w(const Evas_Object_Textblock_Item *it,
8664 Evas_Coord *x, Evas_Coord *w, Eina_Bool start, Eina_Bool switch_items)
8666 if ((start && !switch_items) || (!start && switch_items))
8669 if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8670 _ITEM_TEXT(it)->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8672 ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8673 _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
8687 if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8688 _ITEM_TEXT(it)->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8690 ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8691 _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
8708 * Returns the geometry of the range in line ln. Cur1 is the start cursor,
8709 * cur2 is the end cursor, NULL means from the start or to the end accordingly.
8710 * Assumes that ln is valid, and that at least one of cur1 and cur2 is not NULL.
8712 * @param ln the line to work on.
8713 * @param cur1 the start cursor
8714 * @param cur2 the end cursor
8715 * @return Returns the geometry of the range
8718 _evas_textblock_cursor_range_in_line_geometry_get(
8719 const Evas_Object_Textblock_Line *ln, const Evas_Textblock_Cursor *cur1,
8720 const Evas_Textblock_Cursor *cur2)
8722 Evas_Object_Textblock_Item *it;
8723 Evas_Object_Textblock_Item *it1, *it2;
8724 Eina_List *rects = NULL;
8725 Evas_Textblock_Rectangle *tr;
8727 Eina_Bool switch_items;
8728 const Evas_Textblock_Cursor *cur;
8730 cur = (cur1) ? cur1 : cur2;
8732 if (!cur) return NULL;
8734 /* Find the first and last items */
8737 EINA_INLIST_FOREACH(ln->items, it)
8740 item_len = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
8741 _ITEM_TEXT(it)->text_props.text_len
8743 if ((!cur1 || (cur1->pos < it->text_pos + item_len)) &&
8744 (!cur2 || (cur2->pos >= it->text_pos)))
8749 start = item_len; /* start stores the first item_len */
8752 end = item_len; /* end stores the last item_len */
8756 /* If we couldn't find even one item, return */
8757 if (!it1) return NULL;
8759 /* If the first item is logically before or equal the second item
8760 * we have to set start and end differently than in the other case */
8761 if (it1->text_pos <= it2->text_pos)
8763 start = (cur1) ? (cur1->pos - it1->text_pos) : 0;
8764 end = (cur2) ? (cur2->pos - it2->text_pos) : end;
8765 switch_items = EINA_FALSE;
8769 start = (cur2) ? (cur2->pos - it1->text_pos) : start;
8770 end = (cur1) ? (cur1->pos - it2->text_pos) : 0;
8771 switch_items = EINA_TRUE;
8774 /* IMPORTANT: Don't use cur1/cur2 past this point (because they probably
8775 * don't make sense anymore. That's why there are start and end),
8776 * unless you know what you are doing */
8778 /* Special case when they share the same item and it's a text item */
8779 if ((it1 == it2) && (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT))
8781 Evas_Coord x1, w1, x2, w2;
8782 Evas_Coord x, w, y, h;
8783 Evas_Object_Textblock_Text_Item *ti;
8786 ti = _ITEM_TEXT(it1);
8787 if (ti->parent.format->font.font)
8789 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8790 ti->parent.format->font.font,
8799 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8800 ti->parent.format->font.font,
8809 /* Make x2 the one on the right */
8823 if (ti->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8836 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8837 rects = eina_list_append(rects, tr);
8838 tr->x = ln->x + it1->x + x;
8839 tr->y = ln->par->y + ln->y;
8844 else if (it1 != it2)
8846 /* Get the middle items */
8847 Evas_Coord min_x, max_x;
8849 it = _ITEM(EINA_INLIST_GET(it1)->next);
8850 min_x = max_x = it->x;
8852 if (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8855 Evas_Object_Textblock_Text_Item *ti;
8857 ti = _ITEM_TEXT(it1);
8859 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8860 ti->parent.format->font.font,
8866 /* BUG! Skip the first item */
8871 _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
8879 _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
8884 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8885 rects = eina_list_append(rects, tr);
8886 tr->x = ln->x + it1->x + x;
8887 tr->y = ln->par->y + ln->y;
8892 while (it && (it != it2))
8894 max_x = it->x + it->adv;
8895 it = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(it)->next;
8899 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8900 rects = eina_list_append(rects, tr);
8901 tr->x = ln->x + min_x;
8902 tr->y = ln->par->y + ln->y;
8904 tr->w = max_x - min_x;
8906 if (it2->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8909 Evas_Object_Textblock_Text_Item *ti;
8911 ti = _ITEM_TEXT(it2);
8913 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8914 ti->parent.format->font.font,
8920 /* BUG! skip the last item */
8925 _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
8933 _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
8938 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8939 rects = eina_list_append(rects, tr);
8940 tr->x = ln->x + it2->x + x;
8941 tr->y = ln->par->y + ln->y;
8950 evas_textblock_cursor_range_geometry_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
8952 Evas_Object_Textblock *o;
8953 Evas_Object_Textblock_Line *ln1, *ln2;
8954 Evas_Object_Textblock_Item *it1, *it2;
8955 Eina_List *rects = NULL;
8956 Evas_Textblock_Rectangle *tr;
8958 if (!cur1 || !cur1->node) return NULL;
8959 if (!cur2 || !cur2->node) return NULL;
8960 if (cur1->obj != cur2->obj) return NULL;
8961 o = (Evas_Object_Textblock *)(cur1->obj->object_data);
8962 if (!o->formatted.valid) _relayout(cur1->obj);
8963 if (evas_textblock_cursor_compare(cur1, cur2) > 0)
8965 const Evas_Textblock_Cursor *tc;
8974 _find_layout_item_match(cur1, &ln1, &it1);
8975 if (!ln1 || !it1) return NULL;
8976 _find_layout_item_match(cur2, &ln2, &it2);
8977 if (!ln2 || !it2) return NULL;
8981 rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
8986 Evas_Object_Textblock_Line *plni, *lni;
8987 Eina_List *rects2 = NULL;
8988 /* Handle the first line */
8989 rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
8992 /* Handle the lines between the first and the last line */
8993 lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(ln1)->next;
8994 if (!lni && (ln1->par != ln2->par))
8996 lni = ((Evas_Object_Textblock_Paragraph *)
8997 EINA_INLIST_GET(ln1->par)->next)->lines;
8999 while (lni && (lni != ln2))
9001 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
9002 rects = eina_list_append(rects, tr);
9004 tr->y = lni->par->y + lni->y;
9008 lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(lni)->next;
9009 if (!lni && (plni->par != ln2->par))
9011 lni = ((Evas_Object_Textblock_Paragraph *)
9012 EINA_INLIST_GET(plni->par)->next)->lines;
9015 rects2 = _evas_textblock_cursor_range_in_line_geometry_get(ln2,
9017 rects = eina_list_merge(rects, rects2);
9023 evas_textblock_cursor_format_item_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
9025 Evas_Object_Textblock *o;
9026 Evas_Object_Textblock_Line *ln = NULL;
9027 Evas_Object_Textblock_Format_Item *fi;
9028 Evas_Object_Textblock_Item *it = NULL;
9029 Evas_Coord x, y, w, h;
9031 if (!cur || !evas_textblock_cursor_format_is_visible_get(cur)) return EINA_FALSE;
9032 o = (Evas_Object_Textblock *)(cur->obj->object_data);
9033 if (!o->formatted.valid) _relayout(cur->obj);
9034 if (!evas_textblock_cursor_format_is_visible_get(cur)) return EINA_FALSE;
9035 _find_layout_item_line_match(cur->obj, cur->node, cur->pos, &ln, &it);
9036 fi = _ITEM_FORMAT(it);
9037 if ((!ln) || (!fi)) return EINA_FALSE;
9038 x = ln->x + fi->parent.x;
9039 y = ln->par->y + ln->y + ln->baseline + fi->y;
9050 evas_textblock_cursor_eol_get(const Evas_Textblock_Cursor *cur)
9052 Eina_Bool ret = EINA_FALSE;
9053 Evas_Textblock_Cursor cur2;
9054 if (!cur) return EINA_FALSE;
9056 cur2.obj = cur->obj;
9057 evas_textblock_cursor_copy(cur, &cur2);
9058 evas_textblock_cursor_line_char_last(&cur2);
9059 if (cur2.pos == cur->pos)
9066 /* general controls */
9068 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)
9070 Evas_Object_Textblock_Line *ln;
9073 ln = _find_layout_line_num(obj, line);
9074 if (!ln) return EINA_FALSE;
9075 if (cx) *cx = ln->x;
9076 if (cy) *cy = ln->par->y + ln->y;
9077 if (cw) *cw = ln->w;
9078 if (ch) *ch = ln->h;
9083 _evas_object_textblock_clear_all(Evas_Object *obj)
9086 Evas_Textblock_Cursor *cur;
9091 _paragraphs_free(obj, o->paragraphs);
9092 o->paragraphs = NULL;
9096 o->cursor->node = NULL;
9098 EINA_LIST_FOREACH(o->cursors, l, cur)
9105 _evas_textblock_changed(o, obj);
9109 evas_object_textblock_clear(Evas_Object *obj)
9112 _evas_object_textblock_clear_all(obj);
9114 /* Force recreation of everything for textblock.
9115 * FIXME: We have the same thing in other places, merge it... */
9116 evas_textblock_cursor_paragraph_first(o->cursor);
9117 evas_textblock_cursor_text_append(o->cursor, "");
9121 evas_object_textblock_size_formatted_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
9124 if (!o->formatted.valid) _relayout(obj);
9125 if (w) *w = o->formatted.w;
9126 if (h) *h = o->formatted.h;
9130 _size_native_calc_line_finalize(const Evas_Object *obj, Eina_List *items,
9131 Evas_Coord *ascent, Evas_Coord *descent, Evas_Coord *w)
9133 Evas_Object_Textblock_Item *it;
9136 it = eina_list_data_get(items);
9141 /* If there are no text items yet, calc ascent/descent
9142 * according to the current format. */
9143 if (*ascent + *descent == 0)
9144 _layout_format_ascent_descent_adjust(obj, ascent, descent,
9149 *w = it->format->margin.l + it->format->margin.r;
9153 /* Adjust all the item sizes according to the final line size,
9154 * and update the x positions of all the items of the line. */
9155 EINA_LIST_FOREACH(items, i, it)
9157 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
9159 Evas_Coord fw, fh, fy;
9161 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
9162 if (!fi->formatme) goto loop_advance;
9163 _layout_calculate_format_item_size(obj, fi, ascent,
9164 descent, &fy, &fw, &fh);
9174 _size_native_calc_paragraph_size(const Evas_Object *obj,
9175 const Evas_Object_Textblock *o,
9176 const Evas_Object_Textblock_Paragraph *par,
9177 Evas_Coord *_w, Evas_Coord *_h)
9180 Evas_Object_Textblock_Item *it;
9181 Eina_List *line_items = NULL;
9182 Evas_Coord w = 0, y = 0, wmax = 0, h = 0, ascent = 0, descent = 0;
9184 EINA_LIST_FOREACH(par->logical_items, i, it)
9186 line_items = eina_list_append(line_items, it);
9187 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
9189 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
9190 if (fi->item && (_IS_LINE_SEPARATOR(fi->item) ||
9191 _IS_PARAGRAPH_SEPARATOR(o, fi->item)))
9193 _size_native_calc_line_finalize(obj, line_items, &ascent,
9196 if (ascent + descent > h)
9197 h = ascent + descent;
9203 ascent = descent = 0;
9204 line_items = eina_list_free(line_items);
9208 Evas_Coord fw, fh, fy;
9209 /* If there are no text items yet, calc ascent/descent
9210 * according to the current format. */
9211 if (it && (ascent + descent == 0))
9212 _layout_format_ascent_descent_adjust(obj, &ascent,
9213 &descent, it->format);
9215 _layout_calculate_format_item_size(obj, fi, &ascent,
9216 &descent, &fy, &fw, &fh);
9221 _layout_format_ascent_descent_adjust(obj, &ascent,
9222 &descent, it->format);
9226 _size_native_calc_line_finalize(obj, line_items, &ascent, &descent, &w);
9228 line_items = eina_list_free(line_items);
9230 /* Do the last addition */
9231 if (ascent + descent > h)
9232 h = ascent + descent;
9242 evas_object_textblock_size_native_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
9245 if (!o->native.valid)
9247 Evas_Coord wmax = 0, hmax = 0;
9248 Evas_Object_Textblock_Paragraph *par;
9249 /* We just want the layout objects to update, should probably
9251 if (!o->formatted.valid) _relayout(obj);
9252 EINA_INLIST_FOREACH(o->paragraphs, par)
9255 _size_native_calc_paragraph_size(obj, o, par, &tw, &th);
9264 o->native.valid = 1;
9265 o->content_changed = 0;
9266 o->format_changed = EINA_FALSE;
9268 if (w) *w = o->native.w;
9269 if (h) *h = o->native.h;
9273 evas_object_textblock_style_insets_get(const Evas_Object *obj, Evas_Coord *l, Evas_Coord *r, Evas_Coord *t, Evas_Coord *b)
9276 if (!o->formatted.valid) _relayout(obj);
9277 if (l) *l = o->style_pad.l;
9278 if (r) *r = o->style_pad.r;
9279 if (t) *t = o->style_pad.t;
9280 if (b) *b = o->style_pad.b;
9284 * FIXME: DELETE ME! DELETE ME!
9285 * This is an ugly workaround to get around the fact that
9286 * evas_object_textblock_coords_recalc isn't really called when it's supposed
9287 * to. When that bug is fixed please remove this. */
9289 _workaround_object_coords_recalc(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
9291 evas_object_textblock_coords_recalc(obj);
9294 /* all nice and private */
9296 evas_object_textblock_init(Evas_Object *obj)
9298 Evas_Object_Textblock *o;
9299 #ifdef HAVE_LINEBREAK
9300 static Eina_Bool linebreak_init = EINA_FALSE;
9301 if (!linebreak_init)
9303 linebreak_init = EINA_TRUE;
9309 /* alloc image ob, setup methods and default values */
9310 obj->object_data = evas_object_textblock_new();
9311 /* set up default settings for this kind of object */
9312 obj->cur.color.r = 255;
9313 obj->cur.color.g = 255;
9314 obj->cur.color.b = 255;
9315 obj->cur.color.a = 255;
9316 obj->cur.geometry.x = 0.0;
9317 obj->cur.geometry.y = 0.0;
9318 obj->cur.geometry.w = 0.0;
9319 obj->cur.geometry.h = 0.0;
9321 /* set up object-specific settings */
9322 obj->prev = obj->cur;
9323 /* set up methods (compulsory) */
9324 obj->func = &object_func;
9327 o = (Evas_Object_Textblock *)(obj->object_data);
9328 o->cursor->obj = obj;
9329 evas_object_textblock_text_markup_set(obj, "");
9331 o->legacy_newline = EINA_TRUE;
9332 evas_object_event_callback_priority_add(obj, EVAS_CALLBACK_RESIZE, -1000,
9333 _workaround_object_coords_recalc, NULL);
9337 evas_object_textblock_new(void)
9339 Evas_Object_Textblock *o;
9341 /* alloc obj private data */
9342 EVAS_MEMPOOL_INIT(_mp_obj, "evas_object_textblock", Evas_Object_Textblock, 8, NULL);
9343 o = EVAS_MEMPOOL_ALLOC(_mp_obj, Evas_Object_Textblock);
9344 if (!o) return NULL;
9345 EVAS_MEMPOOL_PREP(_mp_obj, o, Evas_Object_Textblock);
9346 o->magic = MAGIC_OBJ_TEXTBLOCK;
9347 o->cursor = calloc(1, sizeof(Evas_Textblock_Cursor));
9348 _format_command_init();
9353 evas_object_textblock_free(Evas_Object *obj)
9355 Evas_Object_Textblock *o;
9357 _evas_object_textblock_clear_all(obj);
9358 evas_object_textblock_style_set(obj, NULL);
9359 while (evas_object_textblock_style_user_peek(obj))
9361 evas_object_textblock_style_user_pop(obj);
9363 o = (Evas_Object_Textblock *)(obj->object_data);
9367 Evas_Textblock_Cursor *cur;
9369 cur = (Evas_Textblock_Cursor *)o->cursors->data;
9370 o->cursors = eina_list_remove_list(o->cursors, o->cursors);
9373 if (o->repch) eina_stringshare_del(o->repch);
9374 if (o->ellip_ti) _item_free(obj, NULL, _ITEM(o->ellip_ti));
9376 EVAS_MEMPOOL_FREE(_mp_obj, o);
9377 _format_command_shutdown();
9382 evas_object_textblock_render(Evas_Object *obj, void *output, void *context, void *surface, int x, int y)
9384 Evas_Object_Textblock_Paragraph *par, *start = NULL;
9385 Evas_Object_Textblock_Line *ln;
9386 Evas_Object_Textblock *o;
9388 int cx, cy, cw, ch, clip;
9389 const char vals[5][5] =
9398 /* render object to surface with context, and offxet by x,y */
9399 o = (Evas_Object_Textblock *)(obj->object_data);
9400 obj->layer->evas->engine.func->context_multiplier_unset(output,
9402 /* FIXME: This clipping is just until we fix inset handling correctly. */
9403 ENFN->context_clip_clip(output, context,
9404 obj->cur.geometry.x + x,
9405 obj->cur.geometry.y + y,
9406 obj->cur.geometry.w,
9407 obj->cur.geometry.h);
9408 clip = ENFN->context_clip_get(output, context, &cx, &cy, &cw, &ch);
9409 /* If there are no paragraphs and thus there are no lines,
9410 * there's nothing left to do. */
9411 if (!o->paragraphs) return;
9413 #define ITEM_WALK() \
9414 EINA_INLIST_FOREACH(start, par) \
9416 if (!par->visible) continue; \
9419 if ((obj->cur.geometry.y + y + par->y + par->h) < (cy - 20)) \
9421 if ((obj->cur.geometry.y + y + par->y) > (cy + ch + 20)) \
9424 _layout_paragraph_render(o, par); \
9425 EINA_INLIST_FOREACH(par->lines, ln) \
9427 Evas_Object_Textblock_Item *itr; \
9431 if ((obj->cur.geometry.y + y + par->y + ln->y + ln->h) < (cy - 20)) \
9433 if ((obj->cur.geometry.y + y + par->y + ln->y) > (cy + ch + 20)) \
9436 EINA_INLIST_FOREACH(ln->items, itr) \
9439 yoff = ln->baseline; \
9440 if (itr->format->valign != -1.0) \
9442 yoff += itr->format->valign * (ln->h - itr->h); \
9446 if ((obj->cur.geometry.x + x + ln->x + itr->x + itr->w) < (cx - 20)) \
9448 if ((obj->cur.geometry.x + x + ln->x + itr->x) > (cx + cw + 20)) \
9451 if ((ln->x + itr->x + itr->w) <= 0) continue; \
9452 if (ln->x + itr->x > obj->cur.geometry.w) break; \
9455 #define ITEM_WALK_END() \
9461 #define COLOR_SET(col) \
9462 ENFN->context_color_set(output, context, \
9463 (obj->cur.cache.clip.r * ti->parent.format->color.col.r) / 255, \
9464 (obj->cur.cache.clip.g * ti->parent.format->color.col.g) / 255, \
9465 (obj->cur.cache.clip.b * ti->parent.format->color.col.b) / 255, \
9466 (obj->cur.cache.clip.a * ti->parent.format->color.col.a) / 255);
9467 #define COLOR_SET_AMUL(col, amul) \
9468 ENFN->context_color_set(output, context, \
9469 (obj->cur.cache.clip.r * ti->parent.format->color.col.r * (amul)) / 65025, \
9470 (obj->cur.cache.clip.g * ti->parent.format->color.col.g * (amul)) / 65025, \
9471 (obj->cur.cache.clip.b * ti->parent.format->color.col.b * (amul)) / 65025, \
9472 (obj->cur.cache.clip.a * ti->parent.format->color.col.a * (amul)) / 65025);
9473 #define DRAW_TEXT(ox, oy) \
9474 if (ti->parent.format->font.font) ENFN->font_draw(output, context, surface, ti->parent.format->font.font, \
9475 obj->cur.geometry.x + ln->x + ti->parent.x + x + (ox), \
9476 obj->cur.geometry.y + ln->par->y + ln->y + yoff + y + (oy), \
9477 ti->parent.w, ti->parent.h, ti->parent.w, ti->parent.h, \
9481 #define DRAW_RECT(ox, oy, ow, oh, or, og, ob, oa) \
9484 ENFN->context_color_set(output, \
9486 (obj->cur.cache.clip.r * or) / 255, \
9487 (obj->cur.cache.clip.g * og) / 255, \
9488 (obj->cur.cache.clip.b * ob) / 255, \
9489 (obj->cur.cache.clip.a * oa) / 255); \
9490 ENFN->rectangle_draw(output, \
9493 obj->cur.geometry.x + ln->x + x + (ox), \
9494 obj->cur.geometry.y + ln->par->y + ln->y + y + (oy), \
9500 #define DRAW_FORMAT_DASHED(oname, oy, oh, dw, dp) \
9503 if (itr->format->oname) \
9505 unsigned char _or, _og, _ob, _oa; \
9506 int _ind, _dx = 0, _dn, _dr; \
9507 _or = itr->format->color.oname.r; \
9508 _og = itr->format->color.oname.g; \
9509 _ob = itr->format->color.oname.b; \
9510 _oa = itr->format->color.oname.a; \
9511 if (!EINA_INLIST_GET(itr)->next) \
9513 _dn = itr->w / (dw + dp); \
9514 _dr = itr->w % (dw + dp); \
9518 _dn = itr->adv / (dw + dp); \
9519 _dr = itr->adv % (dw + dp); \
9521 if (_dr > dw) _dr = dw; \
9522 for (_ind = 0 ; _ind < _dn ; _ind++) \
9524 DRAW_RECT(itr->x + _dx, oy, dw, oh, _or, _og, _ob, _oa); \
9527 DRAW_RECT(itr->x + _dx, oy, _dr, oh, _or, _og, _ob, _oa); \
9532 #define DRAW_FORMAT(oname, oy, oh) \
9535 if (itr->format->oname) \
9537 unsigned char _or, _og, _ob, _oa; \
9538 _or = itr->format->color.oname.r; \
9539 _og = itr->format->color.oname.g; \
9540 _ob = itr->format->color.oname.b; \
9541 _oa = itr->format->color.oname.a; \
9542 if (!EINA_INLIST_GET(itr)->next) \
9544 DRAW_RECT(itr->x, oy, itr->w, oh, _or, _og, _ob, _oa); \
9548 DRAW_RECT(itr->x, oy, itr->adv, oh, _or, _og, _ob, _oa); \
9555 Evas_Coord look_for_y = 0 - (obj->cur.geometry.y + y);
9558 Evas_Coord tmp_lfy = cy - (obj->cur.geometry.y + y);
9559 if (tmp_lfy > look_for_y)
9560 look_for_y = tmp_lfy;
9563 if (look_for_y >= 0)
9564 start = _layout_find_paragraph_by_y(o, look_for_y);
9567 start = o->paragraphs;
9572 DRAW_FORMAT(backing, 0, ln->h);
9576 /* There are size adjustments that depend on the styles drawn here back
9577 * in "_text_item_update_sizes" should not modify one without the other. */
9579 /* prepare everything for text draw */
9584 int shad_dst, shad_sz, dx, dy, haveshad;
9585 Evas_Object_Textblock_Text_Item *ti;
9586 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9589 shad_dst = shad_sz = dx = dy = haveshad = 0;
9590 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC)
9592 case EVAS_TEXT_STYLE_SHADOW:
9593 case EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW:
9597 case EVAS_TEXT_STYLE_OUTLINE_SHADOW:
9598 case EVAS_TEXT_STYLE_FAR_SHADOW:
9602 case EVAS_TEXT_STYLE_FAR_SOFT_SHADOW:
9607 case EVAS_TEXT_STYLE_SOFT_SHADOW:
9619 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_SHADOW_DIRECTION)
9621 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT:
9625 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM:
9629 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT:
9633 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT:
9637 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT:
9641 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP:
9645 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT:
9649 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT:
9665 for (j = 0; j < 5; j++)
9667 for (i = 0; i < 5; i++)
9669 if (vals[i][j] != 0)
9671 COLOR_SET_AMUL(shadow, vals[i][j] * 50);
9672 DRAW_TEXT(i - 2 + dx, j - 2 + dy);
9687 Evas_Object_Textblock_Text_Item *ti;
9688 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9691 if (ti->parent.format->style == EVAS_TEXT_STYLE_GLOW)
9693 for (j = 0; j < 5; j++)
9695 for (i = 0; i < 5; i++)
9697 if (vals[i][j] != 0)
9699 COLOR_SET_AMUL(glow, vals[i][j] * 50);
9700 DRAW_TEXT(i - 2, j - 2);
9716 Evas_Object_Textblock_Text_Item *ti;
9717 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9720 if ((ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE) ||
9721 (ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE_SHADOW) ||
9722 (ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW))
9730 else if (ti->parent.format->style == EVAS_TEXT_STYLE_SOFT_OUTLINE)
9732 for (j = 0; j < 5; j++)
9734 for (i = 0; i < 5; i++)
9736 if (((i != 2) || (j != 2)) && (vals[i][j] != 0))
9738 COLOR_SET_AMUL(outline, vals[i][j] * 50);
9739 DRAW_TEXT(i - 2, j - 2);
9747 /* normal text and lines */
9750 Evas_Object_Textblock_Text_Item *ti;
9751 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9760 DRAW_FORMAT(strikethrough, (ln->h / 2), 1);
9763 DRAW_FORMAT(underline, ln->baseline + 1, 1);
9765 /* UNDERLINE DASHED */
9766 DRAW_FORMAT_DASHED(underline_dash, ln->baseline + 1, 1,
9767 ti->parent.format->underline_dash_width,
9768 ti->parent.format->underline_dash_gap);
9771 DRAW_FORMAT(underline2, ln->baseline + 3, 1);
9777 evas_object_textblock_render_pre(Evas_Object *obj)
9779 Evas_Object_Textblock *o;
9782 /* dont pre-render the obj twice! */
9783 if (obj->pre_render_done) return;
9784 obj->pre_render_done = 1;
9785 /* pre-render phase. this does anything an object needs to do just before */
9786 /* rendering. this could mean loading the image data, retrieving it from */
9787 /* elsewhere, decoding video etc. */
9788 /* then when this is done the object needs to figure if it changed and */
9789 /* if so what and where and add the appropriate redraw textblocks */
9790 o = (Evas_Object_Textblock *)(obj->object_data);
9791 if ((o->changed) || (o->content_changed) || (o->format_changed) ||
9792 ((obj->cur.geometry.w != o->last_w) ||
9793 (((o->valign != 0.0) || (o->have_ellipsis)) &&
9794 (obj->cur.geometry.h != o->last_h))))
9798 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9799 is_v = evas_object_is_visible(obj);
9800 was_v = evas_object_was_visible(obj);
9806 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9807 is_v = evas_object_is_visible(obj);
9808 was_v = evas_object_was_visible(obj);
9811 /* if someone is clipping this obj - go calculate the clipper */
9812 if (obj->cur.clipper)
9814 if (obj->cur.cache.clip.dirty)
9815 evas_object_clip_recalc(obj->cur.clipper);
9816 obj->cur.clipper->func->render_pre(obj->cur.clipper);
9818 /* now figure what changed and add draw rects */
9819 /* if it just became visible or invisible */
9820 is_v = evas_object_is_visible(obj);
9821 was_v = evas_object_was_visible(obj);
9824 evas_object_render_pre_visible_change(&obj->layer->evas->clip_changes, obj, is_v, was_v);
9827 if (obj->changed_map)
9829 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes,
9833 /* it's not visible - we accounted for it appearing or not so just abort */
9834 if (!is_v) goto done;
9835 /* clipper changed this is in addition to anything else for obj */
9836 evas_object_render_pre_clipper_change(&obj->layer->evas->clip_changes, obj);
9837 /* if we restacked (layer or just within a layer) and don't clip anyone */
9840 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9843 /* if it changed color */
9844 if ((obj->cur.color.r != obj->prev.color.r) ||
9845 (obj->cur.color.g != obj->prev.color.g) ||
9846 (obj->cur.color.b != obj->prev.color.b) ||
9847 (obj->cur.color.a != obj->prev.color.a))
9849 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9852 /* if it changed geometry - and obviously not visibility or color */
9853 /* calculate differences since we have a constant color fill */
9854 /* we really only need to update the differences */
9855 if ((obj->cur.geometry.x != obj->prev.geometry.x) ||
9856 (obj->cur.geometry.y != obj->prev.geometry.y) ||
9857 (obj->cur.geometry.w != obj->prev.geometry.w) ||
9858 (obj->cur.geometry.h != obj->prev.geometry.h))
9860 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9864 evas_object_render_pre_effect_updates(&obj->layer->evas->clip_changes, obj, is_v, was_v);
9868 evas_object_textblock_render_post(Evas_Object *obj)
9870 /* Evas_Object_Textblock *o; */
9872 /* this moves the current data to the previous state parts of the object */
9873 /* in whatever way is safest for the object. also if we don't need object */
9874 /* data anymore we can free it if the object deems this is a good idea */
9875 /* o = (Evas_Object_Textblock *)(obj->object_data); */
9876 /* remove those pesky changes */
9877 evas_object_clip_changes_clean(obj);
9878 /* move cur to prev safely for object data */
9879 evas_object_cur_prev(obj);
9880 /* o->prev = o->cur; */
9881 /* o->changed = 0; */
9884 static unsigned int evas_object_textblock_id_get(Evas_Object *obj)
9886 Evas_Object_Textblock *o;
9888 o = (Evas_Object_Textblock *)(obj->object_data);
9890 return MAGIC_OBJ_TEXTBLOCK;
9893 static unsigned int evas_object_textblock_visual_id_get(Evas_Object *obj)
9895 Evas_Object_Textblock *o;
9897 o = (Evas_Object_Textblock *)(obj->object_data);
9899 return MAGIC_OBJ_CUSTOM;
9902 static void *evas_object_textblock_engine_data_get(Evas_Object *obj)
9904 Evas_Object_Textblock *o;
9906 o = (Evas_Object_Textblock *)(obj->object_data);
9907 if (!o) return NULL;
9908 return o->engine_data;
9912 evas_object_textblock_is_opaque(Evas_Object *obj __UNUSED__)
9914 /* this returns 1 if the internal object data implies that the object is */
9915 /* currently fulyl opque over the entire gradient it occupies */
9920 evas_object_textblock_was_opaque(Evas_Object *obj __UNUSED__)
9922 /* this returns 1 if the internal object data implies that the object was */
9923 /* currently fulyl opque over the entire gradient it occupies */
9928 evas_object_textblock_coords_recalc(Evas_Object *obj)
9930 Evas_Object_Textblock *o;
9932 o = (Evas_Object_Textblock *)(obj->object_data);
9933 if ((obj->cur.geometry.w != o->last_w) ||
9934 (((o->valign != 0.0) || (o->have_ellipsis)) &&
9935 (obj->cur.geometry.h != o->last_h)))
9937 o->formatted.valid = 0;
9943 evas_object_textblock_scale_update(Evas_Object *obj)
9945 Evas_Object_Textblock *o;
9947 o = (Evas_Object_Textblock *)(obj->object_data);
9948 _evas_textblock_invalidate_all(o);
9949 _evas_textblock_changed(o, obj);
9953 _evas_object_textblock_rehint(Evas_Object *obj)
9955 Evas_Object_Textblock *o;
9956 Evas_Object_Textblock_Paragraph *par;
9957 Evas_Object_Textblock_Line *ln;
9959 o = (Evas_Object_Textblock *)(obj->object_data);
9960 EINA_INLIST_FOREACH(o->paragraphs, par)
9962 EINA_INLIST_FOREACH(par->lines, ln)
9964 Evas_Object_Textblock_Item *it;
9966 EINA_INLIST_FOREACH(ln->items, it)
9968 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
9970 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
9971 if (ti->parent.format->font.font)
9973 evas_font_load_hinting_set(obj->layer->evas,
9974 ti->parent.format->font.font,
9975 obj->layer->evas->hinting);
9981 _evas_textblock_invalidate_all(o);
9982 _evas_textblock_changed(o, obj);
9990 /* return EINA_FALSE on error, used in unit_testing */
9992 _evas_textblock_check_item_node_link(Evas_Object *obj)
9994 Evas_Object_Textblock *o;
9995 Evas_Object_Textblock_Paragraph *par;
9996 Evas_Object_Textblock_Line *ln;
9997 Evas_Object_Textblock_Item *it;
9999 o = (Evas_Object_Textblock *)(obj->object_data);
10000 if (!o) return EINA_FALSE;
10002 if (!o->formatted.valid) _relayout(obj);
10004 EINA_INLIST_FOREACH(o->paragraphs, par)
10006 EINA_INLIST_FOREACH(par->lines, ln)
10008 EINA_INLIST_FOREACH(ln->items, it)
10010 if (it->text_node != par->text_node)
10019 _evas_textblock_format_offset_get(const Evas_Object_Textblock_Node_Format *n)
10026 /* Good for debugging */
10028 pfnode(Evas_Object_Textblock_Node_Format *n)
10030 printf("Format Node: %p\n", n);
10031 printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
10032 printf("text_node = %p, offset = %u, visible = %d\n", n->text_node, n->offset, n->visible);
10033 printf("'%s'\n", eina_strbuf_string_get(n->format));
10037 ptnode(Evas_Object_Textblock_Node_Text *n)
10039 printf("Text Node: %p\n", n);
10040 printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
10041 printf("format_node = %p\n", n->format_node);
10042 printf("'%ls'\n", eina_ustrbuf_string_get(n->unicode));
10046 pitem(Evas_Object_Textblock_Item *it)
10048 Evas_Object_Textblock_Text_Item *ti;
10049 Evas_Object_Textblock_Format_Item *fi;
10050 printf("Item: %p\n", it);
10051 printf("Type: %s (%d)\n", (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
10052 "TEXT" : "FORMAT", it->type);
10053 printf("Text pos: %d Visual pos: %d\n", it->text_pos,
10054 #ifdef BIDI_SUPPORT
10060 printf("Coords: x = %d w = %d adv = %d\n", (int) it->x, (int) it->w,
10062 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
10064 ti = _ITEM_TEXT(it);
10065 printf("Text: '%*ls'\n", ti->text_props.text_len, GET_ITEM_TEXT(ti));
10069 fi = _ITEM_FORMAT(it);
10070 printf("Format: '%s'\n", fi->item);
10075 ppar(Evas_Object_Textblock_Paragraph *par)
10077 Evas_Object_Textblock_Item *it;
10079 EINA_LIST_FOREACH(par->logical_items, i, it)
10081 printf("***********************\n");