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
66 #include "evas_common.h"
67 #include "evas_private.h"
70 #include "linebreak.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 EVAS_TEXTBLOCK_REPLACEMENT_CHAR 0xFFFC
82 #define _PARAGRAPH_SEPARATOR 0x2029
83 #define EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(ch) \
84 (((ch) == EVAS_TEXTBLOCK_REPLACEMENT_CHAR) || \
87 ((ch) == _PARAGRAPH_SEPARATOR))
89 /* private struct for textblock object internal data */
92 * @typedef Evas_Object_Textblock
93 * The actual textblock object.
95 typedef struct _Evas_Object_Textblock Evas_Object_Textblock;
98 * @typedef Evas_Object_Style_Tag
99 * The structure used for finding style tags.
101 typedef struct _Evas_Object_Style_Tag Evas_Object_Style_Tag;
104 * @typedef Evas_Object_Textblock_Node_Text
107 typedef struct _Evas_Object_Textblock_Node_Text Evas_Object_Textblock_Node_Text;
110 typedef struct _Evas_Object_Textblock_Node_Format Evas_Object_Textblock_Node_Format;
115 * @typedef Evas_Object_Textblock_Paragraph
116 * A layouting paragraph.
118 typedef struct _Evas_Object_Textblock_Paragraph Evas_Object_Textblock_Paragraph;
121 * @typedef Evas_Object_Textblock_Line
124 typedef struct _Evas_Object_Textblock_Line Evas_Object_Textblock_Line;
127 * @typedef Evas_Object_Textblock_Item
130 typedef struct _Evas_Object_Textblock_Item Evas_Object_Textblock_Item;
133 * @typedef Evas_Object_Textblock_Item
134 * A layouting text item.
136 typedef struct _Evas_Object_Textblock_Text_Item Evas_Object_Textblock_Text_Item;
139 * @typedef Evas_Object_Textblock_Format_Item
140 * A layouting format item.
142 typedef struct _Evas_Object_Textblock_Format_Item Evas_Object_Textblock_Format_Item;
145 * @typedef Evas_Object_Textblock_Format
146 * A textblock format.
148 typedef struct _Evas_Object_Textblock_Format Evas_Object_Textblock_Format;
152 * @def IS_AT_END(ti, ind)
153 * Return true if ind is at the end of the text item, false otherwise.
155 #define IS_AT_END(ti, ind) (ind == ti->text_props.text_len)
159 * @def MOVE_PREV_UNTIL(limit, ind)
160 * This decrements ind as long as ind > limit.
162 #define MOVE_PREV_UNTIL(limit, ind) \
165 if ((limit) < (ind)) \
172 * @def MOVE_NEXT_UNTIL(limit, ind)
173 * This increments ind as long as ind < limit
175 #define MOVE_NEXT_UNTIL(limit, ind) \
178 if ((ind) < (limit)) \
185 * @def GET_ITEM_TEXT(ti)
186 * Returns a const reference to the text of the ti (not null terminated).
188 #define GET_ITEM_TEXT(ti) \
189 (((ti)->parent.text_node) ? \
190 (eina_ustrbuf_string_get((ti)->parent.text_node->unicode) + \
191 (ti)->parent.text_pos) : EINA_UNICODE_EMPTY_STRING)
194 * @def _FORMAT_IS_CLOSER_OF(base, closer, closer_len)
195 * Returns true if closer is the closer of base.
197 #define _FORMAT_IS_CLOSER_OF(base, closer, closer_len) \
198 (!strncmp(base + 1, closer, closer_len) && \
199 (!base[closer_len + 1] || \
200 (base[closer_len + 1] == '=') || \
201 _is_white(base[closer_len + 1])))
203 /*FIXME: document the structs and struct items. */
204 struct _Evas_Object_Style_Tag
213 struct _Evas_Object_Textblock_Node_Text
216 Eina_UStrbuf *unicode;
218 Evas_Object_Textblock_Node_Format *format_node;
219 Evas_Object_Textblock_Paragraph *par;
224 struct _Evas_Object_Textblock_Node_Format
228 const char *orig_format;
229 Evas_Object_Textblock_Node_Text *text_node;
231 unsigned char anchor : 2;
232 Eina_Bool visible : 1;
233 Eina_Bool format_change : 1;
237 #define ANCHOR_NONE 0
239 #define ANCHOR_ITEM 2
244 * A convinience macro for casting to a text node.
246 #define _NODE_TEXT(x) ((Evas_Object_Textblock_Node_Text *) (x))
249 * @def _NODE_FORMAT(x)
250 * A convinience macro for casting to a format node.
252 #define _NODE_FORMAT(x) ((Evas_Object_Textblock_Node_Format *) (x))
256 * A convinience macro for casting to a generic item.
258 #define _ITEM(x) ((Evas_Object_Textblock_Item *) (x))
262 * A convinience macro for casting to a text item.
264 #define _ITEM_TEXT(x) ((Evas_Object_Textblock_Text_Item *) (x))
267 * @def _ITEM_FORMAT(x)
268 * A convinience macro for casting to a format item.
270 #define _ITEM_FORMAT(x) ((Evas_Object_Textblock_Format_Item *) (x))
272 struct _Evas_Object_Textblock_Paragraph
275 Evas_Object_Textblock_Line *lines;
276 Evas_Object_Textblock_Node_Text *text_node;
277 Eina_List *logical_items;
278 Evas_BiDi_Paragraph_Props *bidi_props; /* Only valid during layout */
279 Evas_BiDi_Direction direction;
282 Eina_Bool is_bidi : 1;
283 Eina_Bool visible : 1;
284 Eina_Bool rendered : 1;
287 struct _Evas_Object_Textblock_Line
290 Evas_Object_Textblock_Item *items;
291 Evas_Object_Textblock_Paragraph *par;
292 Evas_Coord x, y, w, h;
297 typedef enum _Evas_Textblock_Item_Type
299 EVAS_TEXTBLOCK_ITEM_TEXT,
300 EVAS_TEXTBLOCK_ITEM_FORMAT,
301 } Evas_Textblock_Item_Type;
303 struct _Evas_Object_Textblock_Item
306 Evas_Textblock_Item_Type type;
307 Evas_Object_Textblock_Node_Text *text_node;
308 Evas_Object_Textblock_Format *format;
313 Evas_Coord adv, x, w, h;
314 Eina_Bool merge : 1; /* Indicates whether this
315 item should merge to the
316 previous item or not */
317 Eina_Bool visually_deleted : 1;
318 /* Indicates whether this
319 item is used in the visual
323 struct _Evas_Object_Textblock_Text_Item
325 Evas_Object_Textblock_Item parent;
326 Evas_Text_Props text_props;
328 Evas_Coord x_adjustment; /* Used to indicate by how
329 much we adjusted sizes */
332 struct _Evas_Object_Textblock_Format_Item
334 Evas_Object_Textblock_Item parent;
335 Evas_BiDi_Direction bidi_dir;
338 unsigned char vsize : 2;
339 unsigned char size : 2;
340 Eina_Bool formatme : 1;
343 struct _Evas_Object_Textblock_Format
345 Evas_Object_Textblock_Node_Format *fnode;
349 Evas_Font_Description *fdesc;
356 unsigned char r, g, b, a;
357 } normal, underline, underline2, outline, shadow, glow, glow2, backing,
372 Eina_Bool wrap_word : 1;
373 Eina_Bool wrap_char : 1;
374 Eina_Bool wrap_mixed : 1;
375 Eina_Bool underline : 1;
376 Eina_Bool underline2 : 1;
377 Eina_Bool strikethrough : 1;
378 Eina_Bool backing : 1;
379 Eina_Bool password : 1;
380 Eina_Bool halign_auto : 1;
383 struct _Evas_Textblock_Style
385 const char *style_text;
387 Evas_Object_Style_Tag *tags;
389 Eina_Bool delete_me : 1;
392 struct _Evas_Textblock_Cursor
396 Evas_Object_Textblock_Node_Text *node;
399 /* Size of the index array */
400 #define TEXTBLOCK_PAR_INDEX_SIZE 10
401 struct _Evas_Object_Textblock
404 Evas_Textblock_Style *style;
405 Evas_Textblock_Cursor *cursor;
407 Evas_Object_Textblock_Node_Text *text_nodes;
408 Evas_Object_Textblock_Node_Format *format_nodes;
411 Evas_Object_Textblock_Paragraph *paragraphs;
412 Evas_Object_Textblock_Paragraph *par_index[TEXTBLOCK_PAR_INDEX_SIZE];
414 Evas_Object_Textblock_Text_Item *ellip_ti;
415 Eina_List *anchors_a;
416 Eina_List *anchors_item;
425 const char *bidi_delimiters;
430 Eina_Bool redraw : 1;
431 Eina_Bool changed : 1;
432 Eina_Bool content_changed : 1;
433 Eina_Bool format_changed : 1;
434 Eina_Bool have_ellipsis : 1;
435 Eina_Bool legacy_newline : 1;
438 /* private methods for textblock objects */
439 static void evas_object_textblock_init(Evas_Object *obj);
440 static void *evas_object_textblock_new(void);
441 static void evas_object_textblock_render(Evas_Object *obj, void *output, void *context, void *surface, int x, int y);
442 static void evas_object_textblock_free(Evas_Object *obj);
443 static void evas_object_textblock_render_pre(Evas_Object *obj);
444 static void evas_object_textblock_render_post(Evas_Object *obj);
446 static unsigned int evas_object_textblock_id_get(Evas_Object *obj);
447 static unsigned int evas_object_textblock_visual_id_get(Evas_Object *obj);
448 static void *evas_object_textblock_engine_data_get(Evas_Object *obj);
450 static int evas_object_textblock_is_opaque(Evas_Object *obj);
451 static int evas_object_textblock_was_opaque(Evas_Object *obj);
453 static void evas_object_textblock_coords_recalc(Evas_Object *obj);
455 static void evas_object_textblock_scale_update(Evas_Object *obj);
457 static const Evas_Object_Func object_func =
459 /* methods (compulsory) */
460 evas_object_textblock_free,
461 evas_object_textblock_render,
462 evas_object_textblock_render_pre,
463 evas_object_textblock_render_post,
464 evas_object_textblock_id_get,
465 evas_object_textblock_visual_id_get,
466 evas_object_textblock_engine_data_get,
467 /* these are optional. NULL = nothing */
472 evas_object_textblock_is_opaque,
473 evas_object_textblock_was_opaque,
476 evas_object_textblock_coords_recalc,
477 evas_object_textblock_scale_update,
483 /* the actual api call to add a textblock */
486 Evas_Object_Textblock *o; \
487 MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ); \
490 o = (Evas_Object_Textblock *)(obj->object_data); \
491 MAGIC_CHECK(o, Evas_Object_Textblock, MAGIC_OBJ_TEXTBLOCK); \
495 #define TB_HEAD_RETURN(x) \
496 Evas_Object_Textblock *o; \
497 MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ); \
500 o = (Evas_Object_Textblock *)(obj->object_data); \
501 MAGIC_CHECK(o, Evas_Object_Textblock, MAGIC_OBJ_TEXTBLOCK); \
507 static Eina_Bool _evas_textblock_cursor_is_at_the_end(const Evas_Textblock_Cursor *cur);
508 static void _evas_textblock_node_text_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *n);
509 static void _evas_textblock_node_text_remove_formats_between(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *n, int start, int end);
510 static Evas_Object_Textblock_Node_Format *_evas_textblock_cursor_node_format_before_or_at_pos_get(const Evas_Textblock_Cursor *cur);
511 static size_t _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt);
512 static void _evas_textblock_node_format_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n, int visual_adjustment);
513 static void _evas_textblock_node_format_free(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n);
514 static void _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n);
515 static void _evas_textblock_changed(Evas_Object_Textblock *o, Evas_Object *obj);
516 static void _evas_textblock_invalidate_all(Evas_Object_Textblock *o);
517 static void _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Text *n, size_t start, int offset);
518 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);
523 * Clears the textblock style passed except for the style_text which is replaced.
524 * @param ts The ts to be cleared. Must not be NULL.
525 * @param style_text the style's text.
528 _style_replace(Evas_Textblock_Style *ts, const char *style_text)
530 eina_stringshare_replace(&ts->style_text, style_text);
531 if (ts->default_tag) free(ts->default_tag);
534 Evas_Object_Style_Tag *tag;
536 tag = (Evas_Object_Style_Tag *)ts->tags;
537 ts->tags = (Evas_Object_Style_Tag *)eina_inlist_remove(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
542 ts->default_tag = NULL;
548 * Clears the textblock style passed.
549 * @param ts The ts to be cleared. Must not be NULL.
552 _style_clear(Evas_Textblock_Style *ts)
554 _style_replace(ts, NULL);
559 * Searches inside the tags stored in the style for the tag matching s.
560 * @param ts The ts to be cleared. Must not be NULL.
561 * @param s The tag to be matched.
562 * @param tag_len the length of the tag string.
563 * @param[out] replace_len The length of the replcaement found. - Must not be NULL.
564 * @return The replacement string found.
566 static inline const char *
567 _style_match_tag(Evas_Textblock_Style *ts, const char *s, size_t tag_len, size_t *replace_len)
569 Evas_Object_Style_Tag *tag;
571 EINA_INLIST_FOREACH(ts->tags, tag)
573 if (tag->tag_len != tag_len) continue;
574 if (!strncmp(tag->tag, s, tag_len))
576 *replace_len = tag->replace_len;
586 * Clears all the nodes (text and format) of the textblock object.
587 * @param obj The evas object, must not be NULL.
590 _nodes_clear(const Evas_Object *obj)
592 Evas_Object_Textblock *o;
594 o = (Evas_Object_Textblock *)(obj->object_data);
595 while (o->text_nodes)
597 Evas_Object_Textblock_Node_Text *n;
600 o->text_nodes = _NODE_TEXT(eina_inlist_remove(
601 EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n)));
602 _evas_textblock_node_text_free(n);
604 while (o->format_nodes)
606 Evas_Object_Textblock_Node_Format *n;
609 o->format_nodes = _NODE_FORMAT(eina_inlist_remove(EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n)));
610 _evas_textblock_node_format_free(o, n);
616 * Unrefs and frees (if needed) a textblock format.
617 * @param obj The Evas_Object, Must not be NULL.
618 * @param fmt the format to be cleaned, must not be NULL.
621 _format_unref_free(const Evas_Object *obj, Evas_Object_Textblock_Format *fmt)
624 if (fmt->ref > 0) return;
625 if (fmt->font.fdesc) evas_font_desc_unref(fmt->font.fdesc);
626 if (fmt->font.source) eina_stringshare_del(fmt->font.source);
627 evas_font_free(obj->layer->evas, fmt->font.font);
634 * @param obj The evas object, must not be NULL.
635 * @param ln the layout line on which the item is in, must not be NULL.
636 * @param it the layout item to be freed
639 _item_free(const Evas_Object *obj, Evas_Object_Textblock_Line *ln, Evas_Object_Textblock_Item *it)
641 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
643 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
645 evas_common_text_props_content_unref(&ti->text_props);
649 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
651 if (fi->item) eina_stringshare_del(fi->item);
653 _format_unref_free(obj, it->format);
656 ln->items = (Evas_Object_Textblock_Item *) eina_inlist_remove(
657 EINA_INLIST_GET(ln->items), EINA_INLIST_GET(ln->items));
664 * Free a layout line.
665 * @param obj The evas object, must not be NULL.
666 * @param ln the layout line to be freed, must not be NULL.
669 _line_free(Evas_Object_Textblock_Line *ln)
671 /* Items are freed from the logical list, except for the ellip item */
675 /* table of html escapes (that i can find) this should be ordered with the
676 * most common first as it's a linear search to match - no hash for this.
678 * these are stored as one large string and one additional array that
679 * contains the offsets to the tokens for space efficiency.
683 * @var escape_strings[]
684 * This string consists of NULL terminated pairs of strings, the first of
685 * every pair is an escape and the second is the value of the escape.
687 static const char escape_strings[] =
688 /* most common escaped stuff */
694 " \0" "\xc2\xa0\0"
695 "¡\0" "\xc2\xa1\0"
696 "¢\0" "\xc2\xa2\0"
697 "£\0" "\xc2\xa3\0"
698 "¤\0" "\xc2\xa4\0"
699 "¥\0" "\xc2\xa5\0"
700 "¦\0" "\xc2\xa6\0"
701 "§\0" "\xc2\xa7\0"
702 "¨\0" "\xc2\xa8\0"
703 "©\0" "\xc2\xa9\0"
704 "ª\0" "\xc2\xaa\0"
705 "«\0" "\xc2\xab\0"
706 "¬\0" "\xc2\xac\0"
707 "®\0" "\xc2\xae\0"
708 "¯\0" "\xc2\xaf\0"
709 "°\0" "\xc2\xb0\0"
710 "±\0" "\xc2\xb1\0"
711 "²\0" "\xc2\xb2\0"
712 "³\0" "\xc2\xb3\0"
713 "´\0" "\xc2\xb4\0"
714 "µ\0" "\xc2\xb5\0"
715 "¶\0" "\xc2\xb6\0"
716 "·\0" "\xc2\xb7\0"
717 "¸\0" "\xc2\xb8\0"
718 "¹\0" "\xc2\xb9\0"
719 "º\0" "\xc2\xba\0"
720 "»\0" "\xc2\xbb\0"
721 "¼\0" "\xc2\xbc\0"
722 "½\0" "\xc2\xbd\0"
723 "¾\0" "\xc2\xbe\0"
724 "¿\0" "\xc2\xbf\0"
725 "À\0" "\xc3\x80\0"
726 "Á\0" "\xc3\x81\0"
727 "Â\0" "\xc3\x82\0"
728 "Ã\0" "\xc3\x83\0"
729 "Ä\0" "\xc3\x84\0"
730 "Å\0" "\xc3\x85\0"
731 "&Aelig;\0" "\xc3\x86\0"
732 "Ç\0" "\xc3\x87\0"
733 "È\0" "\xc3\x88\0"
734 "É\0" "\xc3\x89\0"
735 "Ê\0" "\xc3\x8a\0"
736 "Ë\0" "\xc3\x8b\0"
737 "Ì\0" "\xc3\x8c\0"
738 "Í\0" "\xc3\x8d\0"
739 "Î\0" "\xc3\x8e\0"
740 "Ï\0" "\xc3\x8f\0"
741 "&Eth;\0" "\xc3\x90\0"
742 "Ñ\0" "\xc3\x91\0"
743 "Ò\0" "\xc3\x92\0"
744 "Ó\0" "\xc3\x93\0"
745 "Ô\0" "\xc3\x94\0"
746 "Õ\0" "\xc3\x95\0"
747 "Ö\0" "\xc3\x96\0"
748 "×\0" "\xc3\x97\0"
749 "Ø\0" "\xc3\x98\0"
750 "Ù\0" "\xc3\x99\0"
751 "Ú\0" "\xc3\x9a\0"
752 "Û\0" "\xc3\x9b\0"
753 "Ý\0" "\xc3\x9d\0"
754 "&Thorn;\0" "\xc3\x9e\0"
755 "ß\0" "\xc3\x9f\0"
756 "à\0" "\xc3\xa0\0"
757 "á\0" "\xc3\xa1\0"
758 "â\0" "\xc3\xa2\0"
759 "ã\0" "\xc3\xa3\0"
760 "ä\0" "\xc3\xa4\0"
761 "å\0" "\xc3\xa5\0"
762 "æ\0" "\xc3\xa6\0"
763 "ç\0" "\xc3\xa7\0"
764 "è\0" "\xc3\xa8\0"
765 "é\0" "\xc3\xa9\0"
766 "ê\0" "\xc3\xaa\0"
767 "ë\0" "\xc3\xab\0"
768 "ì\0" "\xc3\xac\0"
769 "í\0" "\xc3\xad\0"
770 "î\0" "\xc3\xae\0"
771 "ï\0" "\xc3\xaf\0"
772 "ð\0" "\xc3\xb0\0"
773 "ñ\0" "\xc3\xb1\0"
774 "ò\0" "\xc3\xb2\0"
775 "ó\0" "\xc3\xb3\0"
776 "ô\0" "\xc3\xb4\0"
777 "õ\0" "\xc3\xb5\0"
778 "ö\0" "\xc3\xb6\0"
779 "÷\0" "\xc3\xb7\0"
780 "ø\0" "\xc3\xb8\0"
781 "ù\0" "\xc3\xb9\0"
782 "ú\0" "\xc3\xba\0"
783 "û\0" "\xc3\xbb\0"
784 "ü\0" "\xc3\xbc\0"
785 "ý\0" "\xc3\xbd\0"
786 "þ\0" "\xc3\xbe\0"
787 "ÿ\0" "\xc3\xbf\0"
788 "α\0" "\xce\x91\0"
789 "β\0" "\xce\x92\0"
790 "γ\0" "\xce\x93\0"
791 "δ\0" "\xce\x94\0"
792 "ε\0" "\xce\x95\0"
793 "ζ\0" "\xce\x96\0"
794 "η\0" "\xce\x97\0"
795 "θ\0" "\xce\x98\0"
796 "ι\0" "\xce\x99\0"
797 "κ\0" "\xce\x9a\0"
798 "λ\0" "\xce\x9b\0"
799 "μ\0" "\xce\x9c\0"
800 "ν\0" "\xce\x9d\0"
801 "ξ\0" "\xce\x9e\0"
802 "ο\0" "\xce\x9f\0"
803 "π\0" "\xce\xa0\0"
804 "ρ\0" "\xce\xa1\0"
805 "σ\0" "\xce\xa3\0"
806 "τ\0" "\xce\xa4\0"
807 "υ\0" "\xce\xa5\0"
808 "φ\0" "\xce\xa6\0"
809 "χ\0" "\xce\xa7\0"
810 "ψ\0" "\xce\xa8\0"
811 "ω\0" "\xce\xa9\0"
812 "…\0" "\xe2\x80\xa6\0"
813 "€\0" "\xe2\x82\xac\0"
814 "←\0" "\xe2\x86\x90\0"
815 "↑\0" "\xe2\x86\x91\0"
816 "→\0" "\xe2\x86\x92\0"
817 "↓\0" "\xe2\x86\x93\0"
818 "↔\0" "\xe2\x86\x94\0"
819 "←\0" "\xe2\x87\x90\0"
820 "→\0" "\xe2\x87\x92\0"
821 "∀\0" "\xe2\x88\x80\0"
822 "∃\0" "\xe2\x88\x83\0"
823 "∇\0" "\xe2\x88\x87\0"
824 "∏\0" "\xe2\x88\x8f\0"
825 "∑\0" "\xe2\x88\x91\0"
826 "∧\0" "\xe2\x88\xa7\0"
827 "∨\0" "\xe2\x88\xa8\0"
828 "∫\0" "\xe2\x88\xab\0"
829 "≠\0" "\xe2\x89\xa0\0"
830 "≡\0" "\xe2\x89\xa1\0"
831 "⊕\0" "\xe2\x8a\x95\0"
832 "⊥\0" "\xe2\x8a\xa5\0"
833 "†\0" "\xe2\x80\xa0\0"
834 "‡\0" "\xe2\x80\xa1\0"
835 "•\0" "\xe2\x80\xa2\0"
838 EVAS_MEMPOOL(_mp_obj);
842 * Checks if a char is a whitespace.
843 * @param c the unicode codepoint.
844 * @return EINA_TRUE if the unicode codepoint is a whitespace, EINA_FALSE otherwise.
847 _is_white(Eina_Unicode c)
850 * unicode list of whitespace chars
852 * 0009..000D <control-0009>..<control-000D>
854 * 0085 <control-0085>
855 * 00A0 NO-BREAK SPACE
856 * 1680 OGHAM SPACE MARK
857 * 180E MONGOLIAN VOWEL SEPARATOR
858 * 2000..200A EN QUAD..HAIR SPACE
859 * 2028 LINE SEPARATOR
860 * 2029 PARAGRAPH SEPARATOR
861 * 202F NARROW NO-BREAK SPACE
862 * 205F MEDIUM MATHEMATICAL SPACE
863 * 3000 IDEOGRAPHIC SPACE
867 ((c >= 0x9) && (c <= 0xd)) ||
872 ((c >= 0x2000) && (c <= 0x200a)) ||
885 * Prepends the text between s and p to the main cursor of the object.
887 * @param cur the cursor to prepend to.
888 * @param[in] s start of the string
889 * @param[in] p end of the string
892 _prepend_text_run(Evas_Textblock_Cursor *cur, const char *s, const char *p)
898 ts = alloca(p - s + 1);
899 strncpy(ts, s, p - s);
901 evas_textblock_cursor_text_prepend(cur, ts);
908 * Returns the numeric value of HEX chars for example for ch = 'A'
909 * the function will return 10.
911 * @param ch The HEX char.
912 * @return numeric value of HEX.
915 _hex_string_get(char ch)
917 if ((ch >= '0') && (ch <= '9')) return (ch - '0');
918 else if ((ch >= 'A') && (ch <= 'F')) return (ch - 'A' + 10);
919 else if ((ch >= 'a') && (ch <= 'f')) return (ch - 'a' + 10);
925 * Parses a string of one of the formas:
930 * To the rgba values.
932 * @param[in] str The string to parse - NOT NULL.
933 * @param[out] r The Red value - NOT NULL.
934 * @param[out] g The Green value - NOT NULL.
935 * @param[out] b The Blue value - NOT NULL.
936 * @param[out] a The Alpha value - NOT NULL.
939 _format_color_parse(const char *str, unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a)
944 *r = *g = *b = *a = 0;
946 if (slen == 7) /* #RRGGBB */
948 *r = (_hex_string_get(str[1]) << 4) | (_hex_string_get(str[2]));
949 *g = (_hex_string_get(str[3]) << 4) | (_hex_string_get(str[4]));
950 *b = (_hex_string_get(str[5]) << 4) | (_hex_string_get(str[6]));
953 else if (slen == 9) /* #RRGGBBAA */
955 *r = (_hex_string_get(str[1]) << 4) | (_hex_string_get(str[2]));
956 *g = (_hex_string_get(str[3]) << 4) | (_hex_string_get(str[4]));
957 *b = (_hex_string_get(str[5]) << 4) | (_hex_string_get(str[6]));
958 *a = (_hex_string_get(str[7]) << 4) | (_hex_string_get(str[8]));
960 else if (slen == 4) /* #RGB */
962 *r = _hex_string_get(str[1]);
964 *g = _hex_string_get(str[2]);
966 *b = _hex_string_get(str[3]);
970 else if (slen == 5) /* #RGBA */
972 *r = _hex_string_get(str[1]);
974 *g = _hex_string_get(str[2]);
976 *b = _hex_string_get(str[3]);
978 *a = _hex_string_get(str[4]);
981 *r = (*r * *a) / 255;
982 *g = (*g * *a) / 255;
983 *b = (*b * *a) / 255;
986 /* The refcount for the formats. */
987 static int format_refcount = 0;
988 /* Holders for the stringshares */
989 static const char *fontstr = NULL;
990 static const char *font_fallbacksstr = NULL;
991 static const char *font_sizestr = NULL;
992 static const char *font_sourcestr = NULL;
993 static const char *font_weightstr = NULL;
994 static const char *font_stylestr = NULL;
995 static const char *font_widthstr = NULL;
996 static const char *langstr = NULL;
997 static const char *colorstr = NULL;
998 static const char *underline_colorstr = NULL;
999 static const char *underline2_colorstr = NULL;
1000 static const char *outline_colorstr = NULL;
1001 static const char *shadow_colorstr = NULL;
1002 static const char *glow_colorstr = NULL;
1003 static const char *glow2_colorstr = NULL;
1004 static const char *backing_colorstr = NULL;
1005 static const char *strikethrough_colorstr = NULL;
1006 static const char *alignstr = NULL;
1007 static const char *valignstr = NULL;
1008 static const char *wrapstr = NULL;
1009 static const char *left_marginstr = NULL;
1010 static const char *right_marginstr = NULL;
1011 static const char *underlinestr = NULL;
1012 static const char *strikethroughstr = NULL;
1013 static const char *backingstr = NULL;
1014 static const char *stylestr = NULL;
1015 static const char *tabstopsstr = NULL;
1016 static const char *linesizestr = NULL;
1017 static const char *linerelsizestr = NULL;
1018 static const char *linegapstr = NULL;
1019 static const char *linerelgapstr = NULL;
1020 static const char *itemstr = NULL;
1021 static const char *linefillstr = NULL;
1022 static const char *ellipsisstr = NULL;
1023 static const char *passwordstr = NULL;
1027 * Init the format strings.
1030 _format_command_init(void)
1032 if (format_refcount == 0)
1034 fontstr = eina_stringshare_add("font");
1035 font_fallbacksstr = eina_stringshare_add("font_fallbacks");
1036 font_sizestr = eina_stringshare_add("font_size");
1037 font_sourcestr = eina_stringshare_add("font_source");
1038 font_weightstr = eina_stringshare_add("font_weight");
1039 font_stylestr = eina_stringshare_add("font_style");
1040 font_widthstr = eina_stringshare_add("font_width");
1041 langstr = eina_stringshare_add("lang");
1042 colorstr = eina_stringshare_add("color");
1043 underline_colorstr = eina_stringshare_add("underline_color");
1044 underline2_colorstr = eina_stringshare_add("underline2_color");
1045 outline_colorstr = eina_stringshare_add("outline_color");
1046 shadow_colorstr = eina_stringshare_add("shadow_color");
1047 glow_colorstr = eina_stringshare_add("glow_color");
1048 glow2_colorstr = eina_stringshare_add("glow2_color");
1049 backing_colorstr = eina_stringshare_add("backing_color");
1050 strikethrough_colorstr = eina_stringshare_add("strikethrough_color");
1051 alignstr = eina_stringshare_add("align");
1052 valignstr = eina_stringshare_add("valign");
1053 wrapstr = eina_stringshare_add("wrap");
1054 left_marginstr = eina_stringshare_add("left_margin");
1055 right_marginstr = eina_stringshare_add("right_margin");
1056 underlinestr = eina_stringshare_add("underline");
1057 strikethroughstr = eina_stringshare_add("strikethrough");
1058 backingstr = eina_stringshare_add("backing");
1059 stylestr = eina_stringshare_add("style");
1060 tabstopsstr = eina_stringshare_add("tabstops");
1061 linesizestr = eina_stringshare_add("linesize");
1062 linerelsizestr = eina_stringshare_add("linerelsize");
1063 linegapstr = eina_stringshare_add("linegap");
1064 linerelgapstr = eina_stringshare_add("linerelgap");
1065 itemstr = eina_stringshare_add("item");
1066 linefillstr = eina_stringshare_add("linefill");
1067 ellipsisstr = eina_stringshare_add("ellipsis");
1068 passwordstr = eina_stringshare_add("password");
1075 * Shutdown the format strings.
1078 _format_command_shutdown(void)
1080 if (--format_refcount > 0) return;
1082 eina_stringshare_del(fontstr);
1083 eina_stringshare_del(font_fallbacksstr);
1084 eina_stringshare_del(font_sizestr);
1085 eina_stringshare_del(font_sourcestr);
1086 eina_stringshare_del(font_weightstr);
1087 eina_stringshare_del(font_stylestr);
1088 eina_stringshare_del(font_widthstr);
1089 eina_stringshare_del(langstr);
1090 eina_stringshare_del(colorstr);
1091 eina_stringshare_del(underline_colorstr);
1092 eina_stringshare_del(underline2_colorstr);
1093 eina_stringshare_del(outline_colorstr);
1094 eina_stringshare_del(shadow_colorstr);
1095 eina_stringshare_del(glow_colorstr);
1096 eina_stringshare_del(glow2_colorstr);
1097 eina_stringshare_del(backing_colorstr);
1098 eina_stringshare_del(strikethrough_colorstr);
1099 eina_stringshare_del(alignstr);
1100 eina_stringshare_del(valignstr);
1101 eina_stringshare_del(wrapstr);
1102 eina_stringshare_del(left_marginstr);
1103 eina_stringshare_del(right_marginstr);
1104 eina_stringshare_del(underlinestr);
1105 eina_stringshare_del(strikethroughstr);
1106 eina_stringshare_del(backingstr);
1107 eina_stringshare_del(stylestr);
1108 eina_stringshare_del(tabstopsstr);
1109 eina_stringshare_del(linesizestr);
1110 eina_stringshare_del(linerelsizestr);
1111 eina_stringshare_del(linegapstr);
1112 eina_stringshare_del(linerelgapstr);
1113 eina_stringshare_del(itemstr);
1114 eina_stringshare_del(linefillstr);
1115 eina_stringshare_del(ellipsisstr);
1116 eina_stringshare_del(passwordstr);
1121 * Copies str to dst while removing the \\ char, i.e unescape the escape sequences.
1123 * @param[out] dst the destination string - Should not be NULL.
1124 * @param[in] src the source string - Should not be NULL.
1127 _format_clean_param(char *dst, const char *src)
1133 for (ss = src; *ss; ss++, ds++)
1135 if ((*ss == '\\') && *(ss + 1)) ss++;
1143 * Parses the cmd and parameter and adds the parsed format to fmt.
1145 * @param obj the evas object - should not be NULL.
1146 * @param fmt The format to populate - should not be NULL.
1147 * @param[in] cmd the command to process, should be stringshared.
1148 * @param[in] param the parameter of the command.
1151 _format_command(Evas_Object *obj, Evas_Object_Textblock_Format *fmt, const char *cmd, const char *param)
1156 len = strlen(param);
1157 tmp_param = alloca(len + 1);
1159 _format_clean_param(tmp_param, param);
1161 /* If we are changing the font, create the fdesc. */
1162 if ((cmd == font_weightstr) || (cmd == font_widthstr) ||
1163 (cmd == font_stylestr) || (cmd == langstr) ||
1164 (cmd == fontstr) || (cmd == font_fallbacksstr))
1166 if (!fmt->font.fdesc)
1168 fmt->font.fdesc = evas_font_desc_new();
1170 else if (!fmt->font.fdesc->new)
1172 Evas_Font_Description *old = fmt->font.fdesc;
1173 fmt->font.fdesc = evas_font_desc_dup(fmt->font.fdesc);
1174 if (old) evas_font_desc_unref(old);
1181 evas_font_name_parse(fmt->font.fdesc, tmp_param);
1183 else if (cmd == font_fallbacksstr)
1185 eina_stringshare_replace(&(fmt->font.fdesc->fallbacks), tmp_param);
1187 else if (cmd == font_sizestr)
1191 v = atoi(tmp_param);
1192 if (v != fmt->font.size)
1197 else if (cmd == font_sourcestr)
1199 if ((!fmt->font.source) ||
1200 ((fmt->font.source) && (strcmp(fmt->font.source, tmp_param))))
1202 if (fmt->font.source) eina_stringshare_del(fmt->font.source);
1203 fmt->font.source = eina_stringshare_add(tmp_param);
1206 else if (cmd == font_weightstr)
1208 fmt->font.fdesc->weight = evas_font_style_find(tmp_param,
1209 tmp_param + strlen(tmp_param), EVAS_FONT_STYLE_WEIGHT);
1211 else if (cmd == font_stylestr)
1213 fmt->font.fdesc->slant = evas_font_style_find(tmp_param,
1214 tmp_param + strlen(tmp_param), EVAS_FONT_STYLE_SLANT);
1216 else if (cmd == font_widthstr)
1218 fmt->font.fdesc->width = evas_font_style_find(tmp_param,
1219 tmp_param + strlen(tmp_param), EVAS_FONT_STYLE_WIDTH);
1221 else if (cmd == langstr)
1223 eina_stringshare_replace(&(fmt->font.fdesc->lang), tmp_param);
1225 else if (cmd == colorstr)
1226 _format_color_parse(tmp_param,
1227 &(fmt->color.normal.r), &(fmt->color.normal.g),
1228 &(fmt->color.normal.b), &(fmt->color.normal.a));
1229 else if (cmd == underline_colorstr)
1230 _format_color_parse(tmp_param,
1231 &(fmt->color.underline.r), &(fmt->color.underline.g),
1232 &(fmt->color.underline.b), &(fmt->color.underline.a));
1233 else if (cmd == underline2_colorstr)
1234 _format_color_parse(tmp_param,
1235 &(fmt->color.underline2.r), &(fmt->color.underline2.g),
1236 &(fmt->color.underline2.b), &(fmt->color.underline2.a));
1237 else if (cmd == outline_colorstr)
1238 _format_color_parse(tmp_param,
1239 &(fmt->color.outline.r), &(fmt->color.outline.g),
1240 &(fmt->color.outline.b), &(fmt->color.outline.a));
1241 else if (cmd == shadow_colorstr)
1242 _format_color_parse(tmp_param,
1243 &(fmt->color.shadow.r), &(fmt->color.shadow.g),
1244 &(fmt->color.shadow.b), &(fmt->color.shadow.a));
1245 else if (cmd == glow_colorstr)
1246 _format_color_parse(tmp_param,
1247 &(fmt->color.glow.r), &(fmt->color.glow.g),
1248 &(fmt->color.glow.b), &(fmt->color.glow.a));
1249 else if (cmd == glow2_colorstr)
1250 _format_color_parse(tmp_param,
1251 &(fmt->color.glow2.r), &(fmt->color.glow2.g),
1252 &(fmt->color.glow2.b), &(fmt->color.glow2.a));
1253 else if (cmd == backing_colorstr)
1254 _format_color_parse(tmp_param,
1255 &(fmt->color.backing.r), &(fmt->color.backing.g),
1256 &(fmt->color.backing.b), &(fmt->color.backing.a));
1257 else if (cmd == strikethrough_colorstr)
1258 _format_color_parse(tmp_param,
1259 &(fmt->color.strikethrough.r), &(fmt->color.strikethrough.g),
1260 &(fmt->color.strikethrough.b), &(fmt->color.strikethrough.a));
1261 else if (cmd == alignstr)
1263 if (!strcmp(tmp_param, "auto"))
1265 fmt->halign_auto = EINA_TRUE;
1269 if (!strcmp(tmp_param, "middle")) fmt->halign = 0.5;
1270 else if (!strcmp(tmp_param, "center")) fmt->halign = 0.5;
1271 else if (!strcmp(tmp_param, "left")) fmt->halign = 0.0;
1272 else if (!strcmp(tmp_param, "right")) fmt->halign = 1.0;
1275 char *endptr = NULL;
1276 double val = strtod(tmp_param, &endptr);
1279 while (*endptr && _is_white(*endptr))
1285 if (fmt->halign < 0.0) fmt->halign = 0.0;
1286 else if (fmt->halign > 1.0) fmt->halign = 1.0;
1288 fmt->halign_auto = EINA_FALSE;
1291 else if (cmd == valignstr)
1293 if (!strcmp(tmp_param, "top")) fmt->valign = 0.0;
1294 else if (!strcmp(tmp_param, "middle")) fmt->valign = 0.5;
1295 else if (!strcmp(tmp_param, "center")) fmt->valign = 0.5;
1296 else if (!strcmp(tmp_param, "bottom")) fmt->valign = 1.0;
1297 else if (!strcmp(tmp_param, "baseline")) fmt->valign = -1.0;
1298 else if (!strcmp(tmp_param, "base")) fmt->valign = -1.0;
1301 char *endptr = NULL;
1302 double val = strtod(tmp_param, &endptr);
1305 while (*endptr && _is_white(*endptr))
1311 if (fmt->valign < 0.0) fmt->valign = 0.0;
1312 else if (fmt->valign > 1.0) fmt->valign = 1.0;
1315 else if (cmd == wrapstr)
1317 if (!strcmp(tmp_param, "word"))
1320 fmt->wrap_char = fmt->wrap_mixed = 0;
1322 else if (!strcmp(tmp_param, "char"))
1324 fmt->wrap_word = fmt->wrap_mixed = 0;
1327 else if (!strcmp(tmp_param, "mixed"))
1329 fmt->wrap_word = fmt->wrap_char = 0;
1330 fmt->wrap_mixed = 1;
1334 fmt->wrap_word = fmt->wrap_mixed = fmt->wrap_char = 0;
1337 else if (cmd == left_marginstr)
1339 if (!strcmp(tmp_param, "reset"))
1343 if (tmp_param[0] == '+')
1344 fmt->margin.l += atoi(&(tmp_param[1]));
1345 else if (tmp_param[0] == '-')
1346 fmt->margin.l -= atoi(&(tmp_param[1]));
1348 fmt->margin.l = atoi(tmp_param);
1349 if (fmt->margin.l < 0) fmt->margin.l = 0;
1352 else if (cmd == right_marginstr)
1354 if (!strcmp(tmp_param, "reset"))
1358 if (tmp_param[0] == '+')
1359 fmt->margin.r += atoi(&(tmp_param[1]));
1360 else if (tmp_param[0] == '-')
1361 fmt->margin.r -= atoi(&(tmp_param[1]));
1363 fmt->margin.r = atoi(tmp_param);
1364 if (fmt->margin.r < 0) fmt->margin.r = 0;
1367 else if (cmd == underlinestr)
1369 if (!strcmp(tmp_param, "off"))
1372 fmt->underline2 = 0;
1374 else if ((!strcmp(tmp_param, "on")) ||
1375 (!strcmp(tmp_param, "single")))
1378 fmt->underline2 = 0;
1380 else if (!strcmp(tmp_param, "double"))
1383 fmt->underline2 = 1;
1386 else if (cmd == strikethroughstr)
1388 if (!strcmp(tmp_param, "off"))
1389 fmt->strikethrough = 0;
1390 else if (!strcmp(tmp_param, "on"))
1391 fmt->strikethrough = 1;
1393 else if (cmd == backingstr)
1395 if (!strcmp(tmp_param, "off"))
1397 else if (!strcmp(tmp_param, "on"))
1400 else if (cmd == stylestr)
1402 char *p1, *p2, *p, *pp;
1404 p1 = alloca(len + 1);
1406 p2 = alloca(len + 1);
1409 if (!strstr(tmp_param, ",")) p1 = tmp_param;
1412 /* split string "str1,str2" into p1 and p2 (if we have more than
1413 * 1 str2 eg "str1,str2,str3,str4" then we don't care. p2 just
1414 * ends up being the last one as right now it's only valid to have
1415 * 1 comma and 2 strings */
1417 for (p = tmp_param; *p; p++)
1430 if (!strcmp(p1, "off")) fmt->style = EVAS_TEXT_STYLE_PLAIN;
1431 else if (!strcmp(p1, "none")) fmt->style = EVAS_TEXT_STYLE_PLAIN;
1432 else if (!strcmp(p1, "plain")) fmt->style = EVAS_TEXT_STYLE_PLAIN;
1433 else if (!strcmp(p1, "shadow")) fmt->style = EVAS_TEXT_STYLE_SHADOW;
1434 else if (!strcmp(p1, "outline")) fmt->style = EVAS_TEXT_STYLE_OUTLINE;
1435 else if (!strcmp(p1, "soft_outline")) fmt->style = EVAS_TEXT_STYLE_SOFT_OUTLINE;
1436 else if (!strcmp(p1, "outline_shadow")) fmt->style = EVAS_TEXT_STYLE_OUTLINE_SHADOW;
1437 else if (!strcmp(p1, "outline_soft_shadow")) fmt->style = EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW;
1438 else if (!strcmp(p1, "glow")) fmt->style = EVAS_TEXT_STYLE_GLOW;
1439 else if (!strcmp(p1, "far_shadow")) fmt->style = EVAS_TEXT_STYLE_FAR_SHADOW;
1440 else if (!strcmp(p1, "soft_shadow")) fmt->style = EVAS_TEXT_STYLE_SOFT_SHADOW;
1441 else if (!strcmp(p1, "far_soft_shadow")) fmt->style = EVAS_TEXT_STYLE_FAR_SOFT_SHADOW;
1442 else fmt->style = EVAS_TEXT_STYLE_PLAIN;
1446 if (!strcmp(p2, "bottom_right")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT);
1447 else if (!strcmp(p2, "bottom")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM);
1448 else if (!strcmp(p2, "bottom_left")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT);
1449 else if (!strcmp(p2, "left")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT);
1450 else if (!strcmp(p2, "top_left")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT);
1451 else if (!strcmp(p2, "top")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP);
1452 else if (!strcmp(p2, "top_right")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT);
1453 else if (!strcmp(p2, "right")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT);
1454 else EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT);
1457 else if (cmd == tabstopsstr)
1459 fmt->tabstops = atoi(tmp_param);
1460 if (fmt->tabstops < 1) fmt->tabstops = 1;
1462 else if (cmd == linesizestr)
1464 fmt->linesize = atoi(tmp_param);
1465 fmt->linerelsize = 0.0;
1467 else if (cmd == linerelsizestr)
1469 char *endptr = NULL;
1470 double val = strtod(tmp_param, &endptr);
1473 while (*endptr && _is_white(*endptr))
1477 fmt->linerelsize = val / 100.0;
1479 if (fmt->linerelsize < 0.0) fmt->linerelsize = 0.0;
1483 else if (cmd == linegapstr)
1485 fmt->linegap = atoi(tmp_param);
1486 fmt->linerelgap = 0.0;
1488 else if (cmd == linerelgapstr)
1490 char *endptr = NULL;
1491 double val = strtod(tmp_param, &endptr);
1494 while (*endptr && _is_white(*endptr))
1498 fmt->linerelgap = val / 100.0;
1500 if (fmt->linerelgap < 0.0) fmt->linerelgap = 0.0;
1504 else if (cmd == itemstr)
1506 // itemstr == replacement object items in textblock - inline imges
1509 else if (cmd == linefillstr)
1511 char *endptr = NULL;
1512 double val = strtod(tmp_param, &endptr);
1515 while (*endptr && _is_white(*endptr))
1519 fmt->linefill = val / 100.0;
1520 if (fmt->linefill < 0.0) fmt->linefill = 0.0;
1524 else if (cmd == ellipsisstr)
1526 char *endptr = NULL;
1527 fmt->ellipsis = strtod(tmp_param, &endptr);
1528 if ((fmt->ellipsis < 0.0) || (fmt->ellipsis > 1.0))
1529 fmt->ellipsis = -1.0;
1532 Evas_Object_Textblock *o;
1534 o = (Evas_Object_Textblock *)(obj->object_data);
1535 o->have_ellipsis = 1;
1538 else if (cmd == passwordstr)
1540 if (!strcmp(tmp_param, "off"))
1542 else if (!strcmp(tmp_param, "on"))
1549 * Returns #EINA_TRUE if the item is a format parameter, #EINA_FALSE otherwise.
1551 * @param[in] item the item to check - Not NULL.
1554 _format_is_param(const char *item)
1556 if (strchr(item, '=')) return EINA_TRUE;
1562 * Parse the format item and populate key and val with the stringshares that
1563 * corrospond to the formats parsed.
1564 * It expects item to be of the structure:
1567 * @param[in] item the item to parse - Not NULL.
1568 * @param[out] key where to store the key at - Not NULL.
1569 * @param[out] val where to store the value at - Not NULL.
1572 _format_param_parse(const char *item, const char **key, const char **val)
1574 const char *start, *end, *quote;
1576 start = strchr(item, '=');
1577 *key = eina_stringshare_add_length(item, start - item);
1578 start++; /* Advance after the '=' */
1579 /* If we can find a quote, our new delimiter is a quote, not a space. */
1580 if ((quote = strchr(start, '\'')))
1583 end = strchr(start, '\'');
1587 end = strchr(start, ' ');
1590 /* Null terminate before the spaces */
1593 *val = eina_stringshare_add_length(start, end - start);
1597 *val = eina_stringshare_add(start);
1603 * This function parses the format passed in *s and advances s to point to the
1604 * next format item, while returning the current one as the return value.
1605 * @param s The current and returned position in the format string.
1606 * @return the current item parsed from the string.
1609 _format_parse(const char **s)
1612 const char *s1 = NULL, *s2 = NULL;
1613 Eina_Bool quote = EINA_FALSE;;
1616 if (*p == 0) return NULL;
1621 if (*p != ' ') s1 = p;
1631 if ((p > *s) && (p[-1] != '\\') && (!quote))
1633 if (*p == ' ') s2 = p;
1635 if (*p == 0) s2 = p;
1650 * Parse the format str and populate fmt with the formats found.
1652 * @param obj The evas object - Not NULL.
1653 * @param[out] fmt The format to populate - Not NULL.
1654 * @param[in] str the string to parse.- Not NULL.
1657 _format_fill(Evas_Object *obj, Evas_Object_Textblock_Format *fmt, const char *str)
1664 /* get rid of anything +s or -s off the start of the string */
1665 while ((*s == ' ') || (*s == '+') || (*s == '-')) s++;
1667 while ((item = _format_parse(&s)))
1669 if (_format_is_param(item))
1671 const char *key = NULL, *val = NULL;
1673 _format_param_parse(item, &key, &val);
1674 _format_command(obj, fmt, key, val);
1675 eina_stringshare_del(key);
1676 eina_stringshare_del(val);
1680 /* immediate - not handled here */
1687 * Duplicate a format and return the duplicate.
1689 * @param obj The evas object - Not NULL.
1690 * @param[in] fmt The format to duplicate - Not NULL.
1691 * @return the copy of the format.
1693 static Evas_Object_Textblock_Format *
1694 _format_dup(Evas_Object *obj, const Evas_Object_Textblock_Format *fmt)
1696 Evas_Object_Textblock_Format *fmt2;
1698 fmt2 = calloc(1, sizeof(Evas_Object_Textblock_Format));
1699 memcpy(fmt2, fmt, sizeof(Evas_Object_Textblock_Format));
1701 fmt2->font.fdesc = evas_font_desc_ref(fmt->font.fdesc);
1703 if (fmt->font.source) fmt2->font.source = eina_stringshare_add(fmt->font.source);
1705 /* FIXME: just ref the font here... */
1706 fmt2->font.font = evas_font_load(obj->layer->evas, fmt2->font.fdesc,
1707 fmt2->font.source, (int)(((double) fmt2->font.size) * obj->cur.scale));
1718 * A pack of information that needed to be passed around in the layout engine,
1719 * packed for easier access.
1721 typedef struct _Ctxt Ctxt;
1726 Evas_Object_Textblock *o;
1728 Evas_Object_Textblock_Paragraph *paragraphs;
1729 Evas_Object_Textblock_Paragraph *par;
1730 Evas_Object_Textblock_Line *ln;
1733 Eina_List *format_stack;
1734 Evas_Object_Textblock_Format *fmt;
1739 int maxascent, maxdescent;
1740 int marginl, marginr;
1742 int underline_extend;
1743 int have_underline, have_underline2;
1744 double align, valign;
1745 Eina_Bool align_auto : 1;
1746 Eina_Bool width_changed : 1;
1749 static void _layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Text_Item *ti, Eina_List *rel);
1750 static void _text_item_update_sizes(Ctxt *c, Evas_Object_Textblock_Text_Item *ti);
1751 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);
1754 * Adjust the ascent/descent of the format and context.
1756 * @param maxascent The ascent to update - Not NUL.
1757 * @param maxdescent The descent to update - Not NUL.
1758 * @param fmt The format to adjust - NOT NULL.
1761 _layout_format_ascent_descent_adjust(const Evas_Object *obj,
1762 Evas_Coord *maxascent, Evas_Coord *maxdescent,
1763 Evas_Object_Textblock_Format *fmt)
1765 int ascent, descent;
1769 // ascent = c->ENFN->font_max_ascent_get(c->ENDT, fmt->font.font);
1770 // descent = c->ENFN->font_max_descent_get(c->ENDT, fmt->font.font);
1771 ascent = ENFN->font_ascent_get(ENDT, fmt->font.font);
1772 descent = ENFN->font_descent_get(ENDT, fmt->font.font);
1773 if (fmt->linesize > 0)
1775 if ((ascent + descent) < fmt->linesize)
1777 ascent = ((fmt->linesize * ascent) / (ascent + descent));
1778 descent = fmt->linesize - ascent;
1781 else if (fmt->linerelsize > 0.0)
1783 descent = descent * fmt->linerelsize;
1784 ascent = ascent * fmt->linerelsize;
1786 descent += fmt->linegap;
1787 descent += ((ascent + descent) * fmt->linerelgap);
1788 if (*maxascent < ascent) *maxascent = ascent;
1789 if (*maxdescent < descent) *maxdescent = descent;
1790 if (fmt->linefill > 0.0)
1794 dh = obj->cur.geometry.h - (*maxascent + *maxdescent);
1796 dh = fmt->linefill * dh;
1797 *maxdescent += dh / 2;
1798 *maxascent += dh - (dh / 2);
1799 // FIXME: set flag that says "if heigh changes - reformat"
1806 * Create a new line using the info from the format and update the format
1809 * @param c The context to work on - Not NULL.
1810 * @param fmt The format to use info from - NOT NULL.
1813 _layout_line_new(Ctxt *c, Evas_Object_Textblock_Format *fmt)
1815 c->ln = calloc(1, sizeof(Evas_Object_Textblock_Line));
1816 c->align = fmt->halign;
1817 c->align_auto = fmt->halign_auto;
1818 c->marginl = fmt->margin.l;
1819 c->marginr = fmt->margin.r;
1820 c->par->lines = (Evas_Object_Textblock_Line *)eina_inlist_append(EINA_INLIST_GET(c->par->lines), EINA_INLIST_GET(c->ln));
1822 c->maxascent = c->maxdescent = 0;
1823 c->ln->line_no = -1;
1824 c->ln->par = c->par;
1827 static inline Evas_Object_Textblock_Paragraph *
1828 _layout_find_paragraph_by_y(Evas_Object_Textblock *o, Evas_Coord y)
1830 Evas_Object_Textblock_Paragraph *start, *par;
1833 start = o->paragraphs;
1835 for (i = 0 ; i < TEXTBLOCK_PAR_INDEX_SIZE ; i++)
1837 if (!o->par_index[i] || (o->par_index[i]->y > y))
1841 start = o->par_index[i];
1844 EINA_INLIST_FOREACH(start, par)
1846 if ((par->y <= y) && (y < par->y + par->h))
1853 static inline Evas_Object_Textblock_Paragraph *
1854 _layout_find_paragraph_by_line_no(Evas_Object_Textblock *o, int line_no)
1856 Evas_Object_Textblock_Paragraph *start, *par;
1859 start = o->paragraphs;
1861 for (i = 0 ; i < TEXTBLOCK_PAR_INDEX_SIZE ; i++)
1863 if (!o->par_index[i] || (o->par_index[i]->line_no > line_no))
1867 start = o->par_index[i];
1870 EINA_INLIST_FOREACH(start, par)
1872 Evas_Object_Textblock_Paragraph *npar =
1873 (Evas_Object_Textblock_Paragraph *) EINA_INLIST_GET(par)->next;
1874 if ((par->line_no <= line_no) &&
1875 (!npar || (line_no < npar->line_no)))
1881 /* End of rbtree index functios */
1885 * Create a new layout paragraph.
1886 * If c->par is not NULL, the paragraph is appended/prepended according
1887 * to the append parameter. If it is NULL, the paragraph is appended at
1888 * the end of the list.
1890 * @param c The context to work on - Not NULL.
1891 * @param n the associated text node
1892 * @param append true to append, false to prpend.
1895 _layout_paragraph_new(Ctxt *c, Evas_Object_Textblock_Node_Text *n,
1898 Evas_Object_Textblock_Paragraph *rel_par = c->par;
1899 c->par = calloc(1, sizeof(Evas_Object_Textblock_Paragraph));
1900 if (append || !rel_par)
1901 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
1902 eina_inlist_append_relative(EINA_INLIST_GET(c->paragraphs),
1903 EINA_INLIST_GET(c->par),
1904 EINA_INLIST_GET(rel_par));
1906 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
1907 eina_inlist_prepend_relative(EINA_INLIST_GET(c->paragraphs),
1908 EINA_INLIST_GET(c->par),
1909 EINA_INLIST_GET(rel_par));
1912 c->par->text_node = n;
1915 c->par->line_no = -1;
1916 c->par->visible = 1;
1917 c->o->num_paragraphs++;
1923 * Update bidi paragraph props.
1925 * @param par The paragraph to update
1928 _layout_update_bidi_props(const Evas_Object_Textblock *o,
1929 Evas_Object_Textblock_Paragraph *par)
1933 const Eina_Unicode *text;
1934 int *segment_idxs = NULL;
1935 text = eina_ustrbuf_string_get(par->text_node->unicode);
1937 if (o->bidi_delimiters)
1938 segment_idxs = evas_bidi_segment_idxs_get(text, o->bidi_delimiters);
1940 evas_bidi_paragraph_props_unref(par->bidi_props);
1941 par->bidi_props = evas_bidi_paragraph_props_get(text,
1942 eina_ustrbuf_length_get(par->text_node->unicode),
1944 par->direction = EVAS_BIDI_PARAGRAPH_DIRECTION_IS_RTL(par->bidi_props) ?
1945 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
1946 par->is_bidi = !!par->bidi_props;
1947 if (segment_idxs) free(segment_idxs);
1955 * Free the visual lines in the paragraph (logical items are kept)
1958 _paragraph_clear(const Evas_Object *obj __UNUSED__,
1959 Evas_Object_Textblock_Paragraph *par)
1963 Evas_Object_Textblock_Line *ln;
1965 ln = (Evas_Object_Textblock_Line *) par->lines;
1966 par->lines = (Evas_Object_Textblock_Line *)eina_inlist_remove(EINA_INLIST_GET(par->lines), EINA_INLIST_GET(par->lines));
1973 * Free the layout paragraph and all of it's lines and logical items.
1976 _paragraph_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *par)
1978 Evas_Object_Textblock *o;
1979 o = (Evas_Object_Textblock *)(obj->object_data);
1980 _paragraph_clear(obj, par);
1983 Eina_List *i, *i_prev;
1984 Evas_Object_Textblock_Item *it;
1985 EINA_LIST_FOREACH_SAFE(par->logical_items, i, i_prev, it)
1987 _item_free(obj, NULL, it);
1989 eina_list_free(par->logical_items);
1992 if (par->bidi_props)
1993 evas_bidi_paragraph_props_unref(par->bidi_props);
1995 /* If we are the active par of the text node, set to NULL */
1996 if (par->text_node && (par->text_node->par == par))
1997 par->text_node->par = NULL;
1999 o->num_paragraphs--;
2006 * Clear all the paragraphs from the inlist pars.
2008 * @param obj the evas object - Not NULL.
2009 * @param pars the paragraphs to clean - Not NULL.
2012 _paragraphs_clear(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *pars)
2014 Evas_Object_Textblock_Paragraph *par;
2016 EINA_INLIST_FOREACH(EINA_INLIST_GET(pars), par)
2018 _paragraph_clear(obj, par);
2024 * Free the paragraphs from the inlist pars, the difference between this and
2025 * _paragraphs_clear is that the latter keeps the logical items and the par
2026 * items, while the former frees them as well.
2028 * @param obj the evas object - Not NULL.
2029 * @param pars the paragraphs to clean - Not NULL.
2032 _paragraphs_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *pars)
2034 Evas_Object_Textblock *o;
2035 o = (Evas_Object_Textblock *)(obj->object_data);
2037 o->num_paragraphs = 0;
2041 Evas_Object_Textblock_Paragraph *par;
2043 par = (Evas_Object_Textblock_Paragraph *) pars;
2044 pars = (Evas_Object_Textblock_Paragraph *)eina_inlist_remove(EINA_INLIST_GET(pars), EINA_INLIST_GET(par));
2045 _paragraph_free(obj, par);
2051 * Push fmt to the format stack, if fmt is NULL, will fush a default item.
2053 * @param c the context to work on - Not NULL.
2054 * @param fmt the format to push.
2055 * @see _layout_format_pop()
2057 static Evas_Object_Textblock_Format *
2058 _layout_format_push(Ctxt *c, Evas_Object_Textblock_Format *fmt,
2059 Evas_Object_Textblock_Node_Format *fnode)
2063 fmt = _format_dup(c->obj, fmt);
2064 c->format_stack = eina_list_prepend(c->format_stack, fmt);
2069 fmt = calloc(1, sizeof(Evas_Object_Textblock_Format));
2070 c->format_stack = eina_list_prepend(c->format_stack, fmt);
2073 fmt->halign_auto = EINA_TRUE;
2075 fmt->style = EVAS_TEXT_STYLE_PLAIN;
2078 fmt->linerelsize = 0.0;
2080 fmt->linerelgap = 0.0;
2088 * Pop fmt to the format stack, if there's something in the stack free fmt
2089 * and set it to point to the next item instead, else return fmt.
2091 * @param c the context to work on - Not NULL.
2092 * @param format - the text of the format to free (assured to start with '-').
2093 * @return the next format in the stack, or format if there's none.
2094 * @see _layout_format_push()
2096 static Evas_Object_Textblock_Format *
2097 _layout_format_pop(Ctxt *c, const char *format)
2099 Evas_Object_Textblock_Format *fmt = eina_list_data_get(c->format_stack);
2101 if ((c->format_stack) && (c->format_stack->next))
2103 Eina_List *redo_nodes = NULL;
2104 format++; /* Skip the '-' */
2106 /* Generic pop, should just pop. */
2107 if (((format[0] == ' ') && !format[1]) ||
2110 _format_unref_free(c->obj, fmt);
2112 eina_list_remove_list(c->format_stack, c->format_stack);
2116 size_t len = strlen(format);
2117 Eina_List *i, *i_next;
2118 /* Remove only the matching format. */
2119 EINA_LIST_FOREACH_SAFE(c->format_stack, i, i_next, fmt)
2121 /* Stop when we reach the base item */
2126 eina_list_remove_list(c->format_stack, c->format_stack);
2128 /* Make sure the ending tag matches the starting tag.
2129 * I.e whole of the ending tag matches the start of the
2130 * starting tag, and the starting tag's next char is either
2131 * NULL or white. Skip the starting '+'. */
2132 if (_FORMAT_IS_CLOSER_OF(
2133 fmt->fnode->orig_format, format, len))
2135 _format_unref_free(c->obj, fmt);
2140 redo_nodes = eina_list_prepend(redo_nodes, fmt->fnode);
2141 _format_unref_free(c->obj, fmt);
2146 /* Redo all the nodes needed to be redone */
2148 Evas_Object_Textblock_Node_Format *fnode;
2149 Eina_List *i, *i_next;
2151 EINA_LIST_FOREACH_SAFE(redo_nodes, i, i_next, fnode)
2153 /* FIXME: Actually do something with the new acquired padding,
2154 * the can be different and affect our padding! */
2155 Evas_Coord style_pad_l, style_pad_r, style_pad_t, style_pad_b;
2156 style_pad_l = style_pad_r = style_pad_t = style_pad_b = 0;
2157 redo_nodes = eina_list_remove_list(redo_nodes, i);
2158 fmt = eina_list_data_get(c->format_stack);
2159 _layout_do_format(c->obj, c, &fmt, fnode,
2160 &style_pad_l, &style_pad_r,
2161 &style_pad_t, &style_pad_b, EINA_FALSE);
2165 fmt = eina_list_data_get(c->format_stack);
2172 * Parse item and fill fmt with the item.
2174 * @param c the context to work on - Not NULL.
2175 * @param fmt the format to fill - not null.
2178 _layout_format_value_handle(Ctxt *c, Evas_Object_Textblock_Format *fmt, const char *item)
2180 const char *key = NULL, *val = NULL;
2182 _format_param_parse(item, &key, &val);
2183 if ((key) && (val)) _format_command(c->obj, fmt, key, val);
2184 if (key) eina_stringshare_del(key);
2185 if (val) eina_stringshare_del(val);
2186 c->align = fmt->halign;
2187 c->align_auto = fmt->halign_auto;
2188 c->marginl = fmt->margin.l;
2189 c->marginr = fmt->margin.r;
2192 #define VSIZE_FULL 0
2193 #define VSIZE_ASCENT 1
2201 * Get the current line's alignment from the context.
2203 * @param c the context to work on - Not NULL.
2205 static inline double
2206 _layout_line_align_get(Ctxt *c)
2209 if (c->align_auto && c->ln)
2211 if (c->ln->items && c->ln->items->text_node &&
2212 (c->ln->par->direction == EVAS_BIDI_DIRECTION_RTL))
2230 * Reorder the items in visual order
2232 * @param line the line to reorder
2235 _layout_line_reorder(Evas_Object_Textblock_Line *line)
2237 /*FIXME: do it a bit more efficient - not very efficient ATM. */
2238 Evas_Object_Textblock_Item *it;
2239 EvasBiDiStrIndex *v_to_l = NULL;
2244 if (line->items && line->items->text_node &&
2245 line->par->bidi_props)
2247 Evas_BiDi_Paragraph_Props *props;
2248 props = line->par->bidi_props;
2249 start = end = line->items->text_pos;
2251 /* Find the first and last positions in the line */
2253 EINA_INLIST_FOREACH(line->items, it)
2255 if (it->text_pos < start)
2257 start = it->text_pos;
2262 tlen = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
2263 _ITEM_TEXT(it)->text_props.text_len : 1;
2264 if (it->text_pos + tlen > end)
2266 end = it->text_pos + tlen;
2272 evas_bidi_props_reorder_line(NULL, start, len, props, &v_to_l);
2274 /* Update visual pos */
2276 Evas_Object_Textblock_Item *i;
2280 i->visual_pos = evas_bidi_position_logical_to_visual(
2281 v_to_l, len, i->text_pos - start);
2282 i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(i)->next;
2286 /*FIXME: not very efficient, sort the items arrays. Anyhow, should only
2287 * reorder if it's a bidi paragraph */
2289 Evas_Object_Textblock_Item *i, *j, *min;
2294 EINA_INLIST_FOREACH(i, j)
2296 if (j->visual_pos < min->visual_pos)
2303 line->items = (Evas_Object_Textblock_Item *) eina_inlist_remove(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min));
2304 line->items = (Evas_Object_Textblock_Item *) eina_inlist_prepend_relative(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min), EINA_INLIST_GET(i));
2307 i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(min)->next;
2312 if (v_to_l) free(v_to_l);
2314 EINA_INLIST_FOREACH(line->items, it)
2324 _layout_calculate_format_item_size(const Evas_Object *obj,
2325 const Evas_Object_Textblock_Format_Item *fi,
2326 Evas_Coord *maxascent, Evas_Coord *maxdescent,
2327 Evas_Coord *_y, Evas_Coord *_w, Evas_Coord *_h)
2329 /* Adjust sizes according to current line height/scale */
2339 p = strstr(s, " size=");
2343 if (sscanf(p, "%ix%i", &w, &h) == 2)
2345 w = w * obj->cur.scale;
2346 h = h * obj->cur.scale;
2351 p = strstr((char *) s, " relsize=");
2353 if (sscanf(p, "%ix%i", &w, &h) == 2)
2356 if (fi->vsize == VSIZE_FULL)
2358 sz = *maxdescent + *maxascent;
2360 else if (fi->vsize == VSIZE_ASCENT)
2381 if (h > (*maxdescent + *maxascent))
2383 *maxascent += h - (*maxdescent + *maxascent);
2387 *_y = -(h - *maxdescent);
2423 * Order the items in the line, update it's properties and update it's
2424 * corresponding paragraph.
2426 * @param c the context to work on - Not NULL.
2427 * @param fmt the format to use.
2428 * @param add_line true if we should create a line, false otherwise.
2431 _layout_line_finalize(Ctxt *c, Evas_Object_Textblock_Format *fmt)
2433 Evas_Object_Textblock_Item *it;
2436 /* If there are no text items yet, calc ascent/descent
2437 * according to the current format. */
2438 if (c->maxascent + c->maxdescent == 0)
2439 _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
2440 &c->maxdescent, fmt);
2442 /* Adjust all the item sizes according to the final line size,
2443 * and update the x positions of all the items of the line. */
2444 EINA_INLIST_FOREACH(c->ln->items, it)
2446 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
2448 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
2449 if (!fi->formatme) goto loop_advance;
2450 _layout_calculate_format_item_size(c->obj, fi, &c->maxascent,
2451 &c->maxdescent, &fi->y, &fi->parent.w, &fi->parent.h);
2452 fi->parent.adv = fi->parent.w;
2459 if ((it->x + it->adv) > c->ln->w) c->ln->w = it->x + it->adv;
2462 c->ln->y = (c->y - c->par->y) + c->o->style_pad.t;
2463 c->ln->h = c->maxascent + c->maxdescent;
2464 c->ln->baseline = c->maxascent;
2465 if (c->have_underline2)
2467 if (c->maxdescent < 4) c->underline_extend = 4 - c->maxdescent;
2469 else if (c->have_underline)
2471 if (c->maxdescent < 2) c->underline_extend = 2 - c->maxdescent;
2473 c->ln->line_no = c->line_no - c->ln->par->line_no;
2475 c->y += c->maxascent + c->maxdescent;
2478 c->ln->x = c->marginl + c->o->style_pad.l +
2480 c->o->style_pad.l - c->o->style_pad.r -
2481 c->marginl - c->marginr) * _layout_line_align_get(c));
2485 c->ln->x = c->marginl + c->o->style_pad.l;
2488 c->par->h = c->ln->y + c->ln->h;
2489 if (c->ln->w > c->par->w)
2490 c->par->w = c->ln->w;
2493 Evas_Coord new_wmax = c->ln->w +
2494 c->marginl + c->marginr - (c->o->style_pad.l + c->o->style_pad.r);
2495 if (new_wmax > c->wmax)
2502 * Create a new line and append it to the lines in the context.
2504 * @param c the context to work on - Not NULL.
2505 * @param fmt the format to use.
2506 * @param add_line true if we should create a line, false otherwise.
2509 _layout_line_advance(Ctxt *c, Evas_Object_Textblock_Format *fmt)
2511 _layout_line_finalize(c, fmt);
2512 _layout_line_new(c, fmt);
2517 * Create a new text layout item from the string and the format.
2519 * @param c the context to work on - Not NULL.
2520 * @param fmt the format to use.
2521 * @param str the string to use.
2522 * @param len the length of the string.
2524 static Evas_Object_Textblock_Text_Item *
2525 _layout_text_item_new(Ctxt *c __UNUSED__, Evas_Object_Textblock_Format *fmt)
2527 Evas_Object_Textblock_Text_Item *ti;
2529 ti = calloc(1, sizeof(Evas_Object_Textblock_Text_Item));
2530 ti->parent.format = fmt;
2531 ti->parent.format->ref++;
2532 ti->parent.type = EVAS_TEXTBLOCK_ITEM_TEXT;
2538 * Return the cutoff of the text in the text item.
2540 * @param c the context to work on - Not NULL.
2541 * @param fmt the format to use. - Not NULL.
2542 * @param it the item to check - Not null.
2543 * @return -1 if there is no cutoff (either because there is really none,
2544 * or because of an error), cutoff index on success.
2547 _layout_text_cutoff_get(Ctxt *c, Evas_Object_Textblock_Format *fmt,
2548 const Evas_Object_Textblock_Text_Item *ti)
2553 x = c->w - c->o->style_pad.l - c->o->style_pad.r - c->marginl -
2554 c->marginr - c->x - ti->x_adjustment;
2557 return c->ENFN->font_last_up_to_pos(c->ENDT, fmt->font.font,
2558 &ti->text_props, x, 0);
2565 * Split before cut, and strip if str[cut - 1] is a whitespace.
2567 * @param c the context to work on - Not NULL.
2568 * @param ti the item to cut - not null.
2569 * @param lti the logical list item of the item.
2570 * @param cut the cut index.
2571 * @return the second (newly created) item.
2573 static Evas_Object_Textblock_Text_Item *
2574 _layout_item_text_split_strip_white(Ctxt *c,
2575 Evas_Object_Textblock_Text_Item *ti, Eina_List *lti, size_t cut)
2577 const Eina_Unicode *ts;
2578 Evas_Object_Textblock_Text_Item *new_ti = NULL, *white_ti = NULL;
2580 ts = GET_ITEM_TEXT(ti);
2582 if (!IS_AT_END(ti, cut) && (ti->text_props.text_len > 0))
2584 new_ti = _layout_text_item_new(c, ti->parent.format);
2585 new_ti->parent.text_node = ti->parent.text_node;
2586 new_ti->parent.text_pos = ti->parent.text_pos + cut;
2587 new_ti->parent.merge = EINA_TRUE;
2589 evas_common_text_props_split(&ti->text_props,
2590 &new_ti->text_props, cut);
2591 _layout_text_add_logical_item(c, new_ti, lti);
2594 /* Strip the previous white if needed */
2595 if ((cut >= 1) && _is_white(ts[cut - 1]) && (ti->text_props.text_len > 0))
2599 size_t white_cut = cut - 1;
2600 white_ti = _layout_text_item_new(c, ti->parent.format);
2601 white_ti->parent.text_node = ti->parent.text_node;
2602 white_ti->parent.text_pos = ti->parent.text_pos + white_cut;
2603 white_ti->parent.merge = EINA_TRUE;
2604 white_ti->parent.visually_deleted = EINA_TRUE;
2606 evas_common_text_props_split(&ti->text_props,
2607 &white_ti->text_props, white_cut);
2608 _layout_text_add_logical_item(c, white_ti, lti);
2612 /* Mark this one as the visually deleted. */
2613 ti->parent.visually_deleted = EINA_TRUE;
2617 if (new_ti || white_ti)
2619 _text_item_update_sizes(c, ti);
2626 * Merge item2 into item1 and free item2.
2628 * @param c the context to work on - Not NULL.
2629 * @param item1 the item to copy to
2630 * @param item2 the item to copy from
2633 _layout_item_merge_and_free(Ctxt *c,
2634 Evas_Object_Textblock_Text_Item *item1,
2635 Evas_Object_Textblock_Text_Item *item2)
2637 evas_common_text_props_merge(&item1->text_props,
2638 &item2->text_props);
2640 _text_item_update_sizes(c, item1);
2642 item1->parent.merge = EINA_FALSE;
2643 item1->parent.visually_deleted = EINA_FALSE;
2645 _item_free(c->obj, NULL, _ITEM(item2));
2650 * Calculates an item's size.
2652 * @param c the context
2653 * @param it the item itself.
2656 _text_item_update_sizes(Ctxt *c, Evas_Object_Textblock_Text_Item *ti)
2658 int tw, th, inset, advw;
2659 const Evas_Object_Textblock_Format *fmt = ti->parent.format;
2660 int shad_sz = 0, shad_dst = 0, out_sz = 0;
2661 int dx = 0, minx = 0, maxx = 0, shx1, shx2;
2665 c->ENFN->font_string_size_get(c->ENDT, fmt->font.font,
2666 &ti->text_props, &tw, &th);
2669 inset = c->ENFN->font_inset_get(c->ENDT, fmt->font.font,
2673 advw = c->ENFN->font_h_advance_get(c->ENDT, fmt->font.font,
2677 /* These adjustments are calculated and thus heavily linked to those in
2678 * textblock_render!!! Don't change one without the other. */
2680 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC)
2682 case EVAS_TEXT_STYLE_SHADOW:
2685 case EVAS_TEXT_STYLE_OUTLINE_SHADOW:
2686 case EVAS_TEXT_STYLE_FAR_SHADOW:
2690 case EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW:
2695 case EVAS_TEXT_STYLE_FAR_SOFT_SHADOW:
2699 case EVAS_TEXT_STYLE_SOFT_SHADOW:
2703 case EVAS_TEXT_STYLE_GLOW:
2704 case EVAS_TEXT_STYLE_SOFT_OUTLINE:
2707 case EVAS_TEXT_STYLE_OUTLINE:
2713 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_SHADOW_DIRECTION)
2715 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT:
2716 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT:
2717 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT:
2720 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT:
2721 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT:
2722 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT:
2724 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP:
2725 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM:
2732 shx1 = dx * shad_dst;
2734 shx2 = dx * shad_dst;
2736 if (shx1 < minx) minx = shx1;
2737 if (shx2 > maxx) maxx = shx2;
2739 ti->x_adjustment = maxx - minx;
2742 ti->parent.w = tw + ti->x_adjustment;
2744 ti->parent.adv = advw;
2750 * Adds the item to the list, updates the item's properties (e.g, x,w,h)
2752 * @param c the context
2753 * @param it the item itself.
2754 * @param rel item ti will be appened after, NULL = last.
2757 _layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Text_Item *ti,
2760 _text_item_update_sizes(c, ti);
2762 c->par->logical_items = eina_list_append_relative_list(
2763 c->par->logical_items, ti, rel);
2768 * Appends the text from node n starting at start ending at off to the layout.
2769 * It uses the fmt for the formatting.
2771 * @param c the current context- NOT NULL.
2772 * @param fmt the format to use.
2773 * @param n the text node. - Not null.
2774 * @param start the start position. - in range.
2775 * @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.
2776 * @param repch a replacement char to print instead of the original string, for example, * when working with passwords.
2779 _layout_text_append(Ctxt *c, Evas_Object_Textblock_Format *fmt, Evas_Object_Textblock_Node_Text *n, int start, int off, const char *repch)
2781 const Eina_Unicode *str = EINA_UNICODE_EMPTY_STRING;
2782 const Eina_Unicode *tbase;
2783 Evas_Object_Textblock_Text_Item *ti;
2785 Eina_Unicode urepch = 0;
2787 /* prepare a working copy of the string, either filled by the repch or
2788 * filled with the true values */
2794 /* Figure out if we want to bail, work with an empty string,
2795 * or continue with a slice of the passed string */
2796 len = eina_ustrbuf_length_get(n->unicode);
2797 if (off == 0) return;
2798 else if (off < 0) off = len - start;
2804 else if ((start == 0) && (off == 0) && (orig_off == -1))
2806 /* Special case that means that we need to add an empty
2808 str = EINA_UNICODE_EMPTY_STRING;
2811 else if ((start >= len) || (start + off > len))
2816 /* If we work with a replacement char, create a string which is the same
2817 * but with replacement chars instead of regular chars. */
2818 if ((fmt->password) && (repch) && (eina_ustrbuf_length_get(n->unicode)))
2823 tbase = str = ptr = alloca((off + 1) * sizeof(Eina_Unicode));
2825 urepch = eina_unicode_utf8_get_next(repch, &ind);
2826 for (i = 0 ; i < off; ptr++, i++)
2830 /* Use the string, just cut the relevant parts */
2833 str = eina_ustrbuf_string_get(n->unicode) + start;
2842 /* If there's no parent text node, only create an empty item */
2845 ti = _layout_text_item_new(c, fmt);
2846 ti->parent.text_node = NULL;
2847 ti->parent.text_pos = 0;
2848 _layout_text_add_logical_item(c, ti, NULL);
2855 Evas_Font_Instance *script_fi = NULL;
2856 int script_len, tmp_cut;
2857 Evas_Script_Type script;
2859 script_len = cur_len;
2861 tmp_cut = evas_common_language_script_end_of_run_get(str,
2862 c->par->bidi_props, start + str - tbase, script_len);
2865 script_len = tmp_cut;
2867 cur_len -= script_len;
2869 script = evas_common_language_script_type_get(str, script_len);
2872 while (script_len > 0)
2874 Evas_Font_Instance *cur_fi = NULL;
2875 int run_len = script_len;
2876 ti = _layout_text_item_new(c, fmt);
2877 ti->parent.text_node = n;
2878 ti->parent.text_pos = start + str - tbase;
2880 if (ti->parent.format->font.font)
2882 run_len = c->ENFN->font_run_end_get(c->ENDT,
2883 ti->parent.format->font.font, &script_fi, &cur_fi,
2884 script, str, script_len);
2887 evas_common_text_props_bidi_set(&ti->text_props,
2888 c->par->bidi_props, ti->parent.text_pos);
2889 evas_common_text_props_script_set(&ti->text_props, script);
2893 c->ENFN->font_text_props_info_create(c->ENDT,
2894 cur_fi, str, &ti->text_props, c->par->bidi_props,
2895 ti->parent.text_pos, run_len);
2898 script_len -= run_len;
2900 _layout_text_add_logical_item(c, ti, NULL);
2907 * Add a format item from the format node n and the item item.
2909 * @param c the current context- NOT NULL.
2910 * @param n the source format node - not null.
2911 * @param item the format text.
2913 * @return the new format item.
2915 static Evas_Object_Textblock_Format_Item *
2916 _layout_format_item_add(Ctxt *c, Evas_Object_Textblock_Node_Format *n, const char *item, Evas_Object_Textblock_Format *fmt)
2918 Evas_Object_Textblock_Format_Item *fi;
2920 fi = calloc(1, sizeof(Evas_Object_Textblock_Format_Item));
2921 fi->item = eina_stringshare_add(item);
2922 fi->parent.type = EVAS_TEXTBLOCK_ITEM_FORMAT;
2923 fi->parent.format = fmt;
2924 fi->parent.format->ref++;
2925 c->par->logical_items = eina_list_append(c->par->logical_items, fi);
2928 fi->parent.text_node = n->text_node;
2929 /* FIXME: make it more efficient */
2930 fi->parent.text_pos = _evas_textblock_node_format_pos_get(n);
2932 fi->bidi_dir = (evas_bidi_is_rtl_char(
2935 fi->parent.text_pos)) ?
2936 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
2938 fi->bidi_dir = EVAS_BIDI_DIRECTION_LTR;
2946 * Should be call after we finish filling a format.
2950 _format_finalize(Evas_Object *obj, Evas_Object_Textblock_Format *fmt)
2954 of = fmt->font.font;
2956 fmt->font.font = evas_font_load(obj->layer->evas, fmt->font.fdesc,
2957 fmt->font.source, (int)(((double) fmt->font.size) * obj->cur.scale));
2958 if (of) evas_font_free(obj->layer->evas, of);
2963 * Returns true if the item is a tab
2964 * @def _IS_TAB(item)
2966 #define _IS_TAB(item) \
2967 (!strcmp(item, "\t") || !strcmp(item, "\\t"))
2970 * Returns true if the item is a line spearator, false otherwise
2971 * @def _IS_LINE_SEPARATOR(item)
2973 #define _IS_LINE_SEPARATOR(item) \
2974 (!strcmp(item, "\n") || !strcmp(item, "\\n"))
2977 * Returns true if the item is a paragraph separator, false otherwise
2978 * @def _IS_PARAGRAPH_SEPARATOR(item)
2980 #define _IS_PARAGRAPH_SEPARATOR(o, item) \
2981 (!strcmp(item, "ps") || \
2982 (o->legacy_newline && _IS_LINE_SEPARATOR(item))) /* Paragraph separator */
2986 * Handles a format by processing a format node. It returns the relevant format
2987 * through _fmt and updates the padding through style_pad_*. If needed,
2988 * it creates a format item.
2990 * @param obj the evas object - NOT NULL.
2991 * @param c the current context- NOT NULL.
2992 * @param _fmt the format that holds the result.
2993 * @param n the source format node - not null.
2994 * @param style_pad_l the pad to update.
2995 * @param style_pad_r the pad to update.
2996 * @param style_pad_t the pad to update.
2997 * @param style_pad_b the pad to update.
2998 * @param create_item Create a new format item if true, only process otherwise.
3001 _layout_do_format(const Evas_Object *obj __UNUSED__, Ctxt *c,
3002 Evas_Object_Textblock_Format **_fmt, Evas_Object_Textblock_Node_Format *n,
3003 int *style_pad_l, int *style_pad_r, int *style_pad_t, int *style_pad_b,
3004 Eina_Bool create_item)
3006 Evas_Object_Textblock_Format *fmt = *_fmt;
3007 /* FIXME: comment the algo */
3014 if (!strncmp(s, "+ item ", 7))
3017 // item size=20x10 href=name
3018 // item relsize=20x10 href=name
3019 // item abssize=20x10 href=name
3021 // optional arguments:
3025 // size == item size (modifies line size) - can be multiplied by
3027 // relsize == relative size (height is current font height, width
3028 // modified accordingly keeping aspect)
3029 // abssize == absolute size (modifies line size) - never mulitplied by
3031 // href == name of item - to be found and matched later and used for
3033 Evas_Object_Textblock_Format_Item *fi;
3035 int vsize = 0, size = 0;
3039 //href = strstr(s, " href=");
3040 p = strstr(s, " vsize=");
3044 if (!strncmp(p, "full", 4)) vsize = VSIZE_FULL;
3045 else if (!strncmp(p, "ascent", 6)) vsize = VSIZE_ASCENT;
3047 p = strstr(s, " size=");
3051 if (sscanf(p, "%ix%i", &w, &h) == 2)
3053 /* this is handled somewhere else because it depends
3054 * on the current scaling factor of the object which
3055 * may change and break because the results of this
3056 * function are cached */
3062 p = strstr(s, " absize=");
3066 if (sscanf(p, "%ix%i", &w, &h) == 2)
3073 p = strstr(s, " relsize=");
3076 /* this is handled somewhere else because it depends
3077 * on the line it resides in, which is not defined
3078 * at this point and will change anyway, which will
3079 * break because the results of this function are
3088 fi = _layout_format_item_add(c, n, s, fmt);
3092 /* For formats items it's usually
3093 the same, we don't handle the
3094 special cases yet. */
3095 fi->parent.w = fi->parent.adv = w;
3098 /* Not sure if it's the best handling, but will do it for now. */
3099 fmt = _layout_format_push(c, fmt, n);
3105 Eina_Bool push_fmt = EINA_FALSE;
3108 fmt = _layout_format_push(c, fmt, n);
3110 push_fmt = EINA_TRUE;
3112 else if (s[0] == '-')
3114 fmt = _layout_format_pop(c, n->orig_format);
3117 while ((item = _format_parse(&s)))
3119 if (_format_is_param(item))
3121 /* Only handle it if it's a push format, otherwise,
3122 * don't let overwrite the format stack.. */
3125 _layout_format_value_handle(c, fmt, item);
3128 else if (create_item)
3130 if ((_IS_PARAGRAPH_SEPARATOR(c->o, item)) ||
3131 (_IS_LINE_SEPARATOR(item)))
3133 Evas_Object_Textblock_Format_Item *fi;
3135 fi = _layout_format_item_add(c, n, item, fmt);
3137 fi->parent.w = fi->parent.adv = 0;
3139 else if ((!strcmp(item, "\t")) || (!strcmp(item, "\\t")))
3141 Evas_Object_Textblock_Format_Item *fi;
3143 fi = _layout_format_item_add(c, n, item, fmt);
3144 fi->parent.w = fi->parent.adv = fmt->tabstops;
3149 _format_finalize(c->obj, fmt);
3153 Evas_Coord pad_l, pad_r, pad_t, pad_b;
3154 pad_l = pad_r = pad_t = pad_b = 0;
3155 evas_text_style_pad_get(fmt->style, &pad_l, &pad_r, &pad_t, &pad_b);
3156 if (pad_l > *style_pad_l) *style_pad_l = pad_l;
3157 if (pad_r > *style_pad_r) *style_pad_r = pad_r;
3158 if (pad_t > *style_pad_t) *style_pad_t = pad_t;
3159 if (pad_b > *style_pad_b) *style_pad_b = pad_b;
3162 if (fmt->underline2)
3163 c->have_underline2 = 1;
3164 else if (fmt->underline)
3165 c->have_underline = 1;
3170 _layout_update_par(Ctxt *c)
3172 Evas_Object_Textblock_Paragraph *last_par;
3173 last_par = (Evas_Object_Textblock_Paragraph *)
3174 EINA_INLIST_GET(c->par)->prev;
3177 c->par->y = last_par->y + last_par->h;
3185 /* -1 means no wrap */
3187 _layout_get_charwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3188 const Evas_Object_Textblock_Text_Item *ti, size_t line_start,
3193 size_t len = eina_ustrbuf_length_get(ti->parent.text_node->unicode);
3194 /* Currently not being used, because it doesn't contain relevant
3199 wrap = _layout_text_cutoff_get(c, fmt, ti);
3202 uwrap = (size_t) wrap + ti->parent.text_pos;
3206 if (uwrap == line_start)
3208 uwrap = ti->parent.text_pos +
3209 (size_t) evas_common_text_props_cluster_next(&ti->text_props, wrap);
3211 if ((uwrap <= line_start) || (uwrap > len))
3217 /* -1 means no wrap */
3218 #ifdef HAVE_LINEBREAK
3220 /* Allow break means: if we can break after the current char */
3221 #define ALLOW_BREAK(i) \
3222 (breaks[i] <= LINEBREAK_ALLOWBREAK)
3226 #define ALLOW_BREAK(i) \
3231 _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3232 const Evas_Object_Textblock_Text_Item *ti, Eina_Bool mixed_wrap,
3233 size_t line_start, const char *breaks)
3235 Eina_Bool wrap_after = EINA_FALSE;
3238 const Eina_Unicode *str = eina_ustrbuf_string_get(
3239 ti->parent.text_node->unicode);
3240 int item_start = ti->parent.text_pos;
3241 size_t len = eina_ustrbuf_length_get(ti->parent.text_node->unicode);
3242 #ifndef HAVE_LINEBREAK
3243 /* Not used without liblinebreak ATM. */
3249 swrap = _layout_text_cutoff_get(c, fmt, ti);
3250 /* Avoiding too small textblocks to even contain one char.
3251 * FIXME: This can cause breaking inside ligatures. */
3256 orig_wrap = wrap = swrap + item_start;
3259 if (wrap > line_start)
3261 /* The wrapping point found is the first char of the next string
3262 the rest works on the last char of the previous string.
3263 If it's a whitespace, then it's ok, and no need to go back
3264 because we'll remove it anyway. */
3265 if (!_is_white(str[wrap]))
3266 MOVE_PREV_UNTIL(line_start, wrap);
3267 /* If there's a breakable point inside the text, scan backwards until
3269 while (wrap > line_start)
3271 if (ALLOW_BREAK(wrap))
3276 if ((wrap > line_start) ||
3277 ((wrap == line_start) && (ALLOW_BREAK(wrap)) && (wrap < len)))
3279 /* We found a suitable wrapping point, break here. */
3280 MOVE_NEXT_UNTIL(len, wrap);
3287 return ((orig_wrap >= line_start) && (orig_wrap < len)) ?
3288 ((int) orig_wrap) : -1;
3292 /* Scan forward to find the next wrapping point */
3294 wrap_after = EINA_TRUE;
3299 /* If we need to find the position after the cutting point */
3300 if ((wrap == line_start) || (wrap_after))
3304 return _layout_get_charwrap(c, fmt, ti,
3305 line_start, breaks);
3311 if (ALLOW_BREAK(wrap))
3317 if ((wrap < len) && (wrap > line_start))
3319 MOVE_NEXT_UNTIL(len, wrap);
3332 /* -1 means no wrap */
3334 _layout_get_wordwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3335 const Evas_Object_Textblock_Text_Item *ti, size_t line_start,
3338 return _layout_get_word_mixwrap_common(c, fmt, ti, EINA_FALSE, line_start,
3342 /* -1 means no wrap */
3344 _layout_get_mixedwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3345 const Evas_Object_Textblock_Text_Item *ti, size_t line_start,
3348 return _layout_get_word_mixwrap_common(c, fmt, ti, EINA_TRUE, line_start,
3352 /* Should be moved inside _layout_ellipsis_item_new once we fix the hack in
3353 * textblock render */
3354 static const Eina_Unicode _ellip_str[2] = { 0x2026, '\0' };
3356 static Evas_Object_Textblock_Text_Item *
3357 _layout_ellipsis_item_new(Ctxt *c, const Evas_Object_Textblock_Item *cur_it)
3359 Evas_Object_Textblock_Text_Item *ellip_ti;
3360 Evas_Script_Type script;
3361 Evas_Font_Instance *script_fi = NULL, *cur_fi;
3362 size_t len = 1; /* The length of _ellip_str */
3364 /* We can free it here, cause there's only one ellipsis item per tb. */
3365 if (c->o->ellip_ti) _item_free(c->obj, NULL, _ITEM(c->o->ellip_ti));
3366 c->o->ellip_ti = ellip_ti = _layout_text_item_new(c,
3367 eina_list_data_get(eina_list_last(c->format_stack)));
3368 ellip_ti->parent.text_node = cur_it->text_node;
3369 ellip_ti->parent.text_pos = cur_it->text_pos;
3370 script = evas_common_language_script_type_get(_ellip_str, len);
3372 evas_common_text_props_bidi_set(&ellip_ti->text_props,
3373 c->par->bidi_props, ellip_ti->parent.text_pos);
3374 evas_common_text_props_script_set (&ellip_ti->text_props, script);
3376 if (ellip_ti->parent.format->font.font)
3378 /* It's only 1 char anyway, we don't need the run end. */
3379 (void) c->ENFN->font_run_end_get(c->ENDT,
3380 ellip_ti->parent.format->font.font, &script_fi, &cur_fi,
3381 script, _ellip_str, len);
3383 c->ENFN->font_text_props_info_create(c->ENDT,
3384 cur_fi, _ellip_str, &ellip_ti->text_props,
3385 c->par->bidi_props, ellip_ti->parent.text_pos, len);
3388 _text_item_update_sizes(c, ellip_ti);
3390 if (cur_it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3392 ellip_ti->parent.text_pos += _ITEM_TEXT(cur_it)->text_props.text_len
3397 ellip_ti->parent.text_pos++;
3408 _layout_handle_ellipsis(Ctxt *c, Evas_Object_Textblock_Item *it, Eina_List *i)
3410 Evas_Object_Textblock_Text_Item *ellip_ti, *last_ti;
3411 Evas_Object_Textblock_Item *last_it;
3414 ellip_ti = _layout_ellipsis_item_new(c, it);
3416 last_ti = _ITEM_TEXT(it);
3419 c->w -= ellip_ti->parent.w;
3422 wrap = _layout_text_cutoff_get(c, last_it->format,
3424 if ((wrap > 0) && !IS_AT_END(last_ti, (size_t) wrap))
3426 _layout_item_text_split_strip_white(c, last_ti, i, wrap);
3432 /* We haven't added it yet at this point */
3433 if (_ITEM(last_ti) != it)
3436 _ITEM(EINA_INLIST_GET(last_it)->prev);
3437 c->ln->items = _ITEM(eina_inlist_remove(
3438 EINA_INLIST_GET(c->ln->items),
3439 EINA_INLIST_GET(_ITEM(last_ti))));
3444 _ITEM(EINA_INLIST_GET(c->ln->items)->last);
3446 last_ti = _ITEM_TEXT(last_it);
3449 c->x -= last_it->adv;
3453 while (last_it && (wrap == 0));
3455 c->w += ellip_ti->parent.w;
3456 /* If we should add this item, do it */
3459 c->ln->items = (Evas_Object_Textblock_Item *)
3460 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3461 EINA_INLIST_GET(it));
3462 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3464 Evas_Object_Textblock_Format_Item *fi;
3465 fi = _ITEM_FORMAT(it);
3469 c->ln->items = (Evas_Object_Textblock_Item *)
3470 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3471 EINA_INLIST_GET(_ITEM(ellip_ti)));
3472 _layout_line_finalize(c, ellip_ti->parent.format);
3477 _layout_paragraph_reorder_lines(Evas_Object_Textblock_Paragraph *par)
3479 Evas_Object_Textblock_Line *ln;
3481 EINA_INLIST_FOREACH(EINA_INLIST_GET(par->lines), ln)
3483 _layout_line_reorder(ln);
3489 _layout_paragraph_render(Evas_Object_Textblock *o,
3490 Evas_Object_Textblock_Paragraph *par)
3494 par->rendered = EINA_TRUE;
3499 _layout_update_bidi_props(o, par);
3500 _layout_paragraph_reorder_lines(par);
3501 /* Clear the bidi props because we don't need them anymore. */
3502 if (par->bidi_props)
3504 evas_bidi_paragraph_props_unref(par->bidi_props);
3505 par->bidi_props = NULL;
3511 /* 0 means go ahead, 1 means break without an error, 2 means
3512 * break with an error, should probably clean this a bit (enum/macro)
3515 _layout_par(Ctxt *c)
3517 Evas_Object_Textblock_Item *it;
3521 char *line_breaks = NULL;
3523 if (!c->par->logical_items)
3526 /* We want to show it. */
3527 c->par->visible = 1;
3529 /* Check if we need to skip this paragraph because it's already layouted
3530 * correctly, and mark handled nodes as dirty. */
3531 c->par->line_no = c->line_no;
3532 if (c->par->text_node)
3534 /* Skip this paragraph if width is the same, there is no ellipsis
3535 * and we aren't just calculating. */
3536 if (!c->par->text_node->new && !c->par->text_node->dirty &&
3537 !c->width_changed && c->par->lines &&
3538 !c->o->have_ellipsis)
3540 Evas_Object_Textblock_Line *ln;
3541 /* Update c->line_no */
3542 ln = (Evas_Object_Textblock_Line *)
3543 EINA_INLIST_GET(c->par->lines)->last;
3545 c->line_no = c->par->line_no + ln->line_no + 1;
3548 c->par->text_node->dirty = EINA_FALSE;
3549 c->par->text_node->new = EINA_FALSE;
3550 c->par->rendered = EINA_FALSE;
3552 /* Merge back and clear the paragraph */
3554 Eina_List *itr, *itr_next;
3555 Evas_Object_Textblock_Item *ititr, *prev_it = NULL;
3556 _paragraph_clear(c->obj, c->par);
3557 EINA_LIST_FOREACH_SAFE(c->par->logical_items, itr, itr_next, ititr)
3559 if (ititr->merge && prev_it &&
3560 (prev_it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
3561 (ititr->type == EVAS_TEXTBLOCK_ITEM_TEXT))
3563 _layout_item_merge_and_free(c, _ITEM_TEXT(prev_it),
3565 c->par->logical_items =
3566 eina_list_remove_list(c->par->logical_items, itr);
3578 it = _ITEM(eina_list_data_get(c->par->logical_items));
3579 _layout_line_new(c, it->format);
3580 /* We walk on our own because we want to be able to add items from
3581 * inside the list and then walk them on the next iteration. */
3582 for (i = c->par->logical_items ; i ; )
3586 it = _ITEM(eina_list_data_get(i));
3587 /* Skip visually deleted items */
3588 if (it->visually_deleted)
3590 i = eina_list_next(i);
3594 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3596 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
3597 _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
3598 &c->maxdescent, ti->parent.format);
3602 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
3605 /* If there are no text items yet, calc ascent/descent
3606 * according to the current format. */
3607 if (c->maxascent + c->maxdescent == 0)
3608 _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
3609 &c->maxdescent, it->format);
3611 _layout_calculate_format_item_size(c->obj, fi, &c->maxascent,
3612 &c->maxdescent, &fi->y, &fi->parent.w, &fi->parent.h);
3613 fi->parent.adv = fi->parent.w;
3618 /* Check if we need to wrap, i.e the text is bigger than the width,
3619 or we already found a wrap point. */
3621 (((c->x + it->adv) >
3622 (c->w - c->o->style_pad.l - c->o->style_pad.r -
3623 c->marginl - c->marginr)) || (wrap > 0)))
3625 /* Handle ellipsis here. If we don't have more width left
3626 * and no height left, or no more width left and no wrapping. */
3627 if ((it->format->ellipsis == 1.0) && (c->h >= 0) &&
3628 ((2 * it->h + c->y >
3629 c->h - c->o->style_pad.t - c->o->style_pad.b) ||
3630 (!it->format->wrap_word && !it->format->wrap_char &&
3631 !it->format->wrap_mixed)))
3633 _layout_handle_ellipsis(c, it, i);
3637 /* If we want to wrap and it's worth checking for wrapping
3638 * (i.e there's actually text). */
3639 else if ((it->format->wrap_word || it->format->wrap_char ||
3640 it->format->wrap_mixed) && it->text_node)
3642 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3644 /* Don't wrap if it's the only item */
3647 /*FIXME: I should handle format correctly,
3648 i.e verify we are allowed to break here */
3649 _layout_line_advance(c, it->format);
3655 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
3658 #ifdef HAVE_LINEBREAK
3659 /* If we haven't calculated the linebreaks yet,
3663 /* Only relevant in those cases */
3664 if (it->format->wrap_word || it->format->wrap_mixed)
3667 lang = (it->format->font.fdesc) ?
3668 it->format->font.fdesc->lang : "";
3670 eina_ustrbuf_length_get(
3671 it->text_node->unicode);
3672 line_breaks = malloc(len);
3673 set_linebreaks_utf32((const utf32_t *)
3674 eina_ustrbuf_string_get(
3675 it->text_node->unicode),
3676 len, lang, line_breaks);
3681 line_start = c->ln->items->text_pos;
3683 line_start = ti->parent.text_pos;
3686 /* If we don't already have a wrap point from before */
3689 if (it->format->wrap_word)
3690 wrap = _layout_get_wordwrap(c, it->format, ti,
3691 line_start, line_breaks);
3692 else if (it->format->wrap_char)
3693 wrap = _layout_get_charwrap(c, it->format, ti,
3694 line_start, line_breaks);
3695 else if (it->format->wrap_mixed)
3696 wrap = _layout_get_mixedwrap(c, it->format, ti,
3697 line_start, line_breaks);
3702 /* If it's before the item, rollback and apply.
3703 if it's in the item, cut.
3704 If it's after the item, delay the cut */
3707 size_t uwrap = (size_t) wrap;
3708 if (uwrap < ti->parent.text_pos)
3710 /* Rollback latest additions, and cut that
3712 i = eina_list_prev(i);
3713 it = eina_list_data_get(i);
3714 while (uwrap < it->text_pos)
3716 c->ln->items = _ITEM(
3718 EINA_INLIST_GET(c->ln->items),
3719 EINA_INLIST_GET(it)));
3720 i = eina_list_prev(i);
3721 it = eina_list_data_get(i);
3724 c->ln->items = _ITEM(
3726 EINA_INLIST_GET(c->ln->items),
3727 EINA_INLIST_GET(it)));
3730 /* If it points to the end, it means the previous
3731 * char is a whitespace we should remove, so this
3732 * is a wanted cutting point. */
3733 else if (uwrap > ti->parent.text_pos +
3734 ti->text_props.text_len)
3735 wrap = -1; /* Delay the cut in a smart way
3736 i.e use the item_pos as the line_start, because
3737 there's already no cut before*/
3739 wrap -= ti->parent.text_pos; /* Cut here */
3744 _layout_item_text_split_strip_white(c, ti, i, wrap);
3748 /* Should wrap before the item */
3751 _layout_line_advance(c, it->format);
3759 if (!redo_item && !it->visually_deleted)
3761 c->ln->items = (Evas_Object_Textblock_Item *)
3762 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3763 EINA_INLIST_GET(it));
3764 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3766 Evas_Object_Textblock_Format_Item *fi;
3767 fi = _ITEM_FORMAT(it);
3769 /* If it's a newline, and we are not in newline compat
3770 * mode, or we are in newline compat mode, and this is
3771 * not used as a paragraph separator, advance */
3772 if (fi->item && _IS_LINE_SEPARATOR(fi->item) &&
3773 (!c->o->legacy_newline ||
3780 i = eina_list_next(i);
3784 /* Each line is according to the first item in it, and here
3785 * i is already the next item (or the current if we redo it) */
3788 it = _ITEM(eina_list_data_get(i));
3790 _layout_line_advance(c, it->format);
3795 /* Here 'it' is the last format used */
3796 _layout_line_finalize(c, it->format);
3800 #ifdef HAVE_LINEBREAK
3810 * Invalidate text nodes according to format changes
3811 * This goes through all the new format changes and marks the text nodes
3812 * that should be invalidated because of format changes.
3814 * @param c the working context.
3817 _format_changes_invalidate_text_nodes(Ctxt *c)
3819 Evas_Object_Textblock_Node_Format *fnode = c->o->format_nodes;
3820 Evas_Object_Textblock_Node_Text *start_n = NULL;
3821 Eina_List *fstack = NULL;
3827 const char *fstr = fnode->orig_format;
3828 /* balance < 0 means we gave up and everything should be
3834 start_n = fnode->text_node;
3835 fstack = eina_list_prepend(fstack, fnode);
3837 else if (*fstr == '-')
3842 fstr_len = strlen(fstr);
3843 /* Generic popper, just pop */
3844 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
3846 fstack = eina_list_remove_list(fstack, fstack);
3849 /* Find the matching format and pop it, if the matching format
3850 * is out format, i.e the last one, pop and break. */
3854 Evas_Object_Textblock_Node_Format *fnode2;
3855 EINA_LIST_FOREACH(fstack, i, fnode2)
3857 if (_FORMAT_IS_CLOSER_OF(
3858 fnode2->orig_format, fstr, fstr_len))
3860 fstack = eina_list_remove_list(fstack, i);
3869 Evas_Object_Textblock_Node_Text *f_tnode =
3873 start_n->dirty = EINA_TRUE;
3874 if (start_n == f_tnode)
3877 _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
3882 else if (!fnode->visible)
3887 /* if we don't already have a starting point, use the
3888 * current paragraph. */
3890 start_n = fnode->text_node;
3894 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
3901 start_n->dirty = EINA_TRUE;
3902 start_n = _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
3908 /** FIXME: Document */
3910 _layout_pre(Ctxt *c, int *style_pad_l, int *style_pad_r, int *style_pad_t,
3913 Evas_Object *obj = c->obj;
3914 Evas_Object_Textblock *o = c->o;
3915 /* Mark text nodes as dirty if format have changed. */
3916 if (c->o->format_changed)
3918 _format_changes_invalidate_text_nodes(c);
3921 if (o->content_changed)
3923 Evas_Object_Textblock_Node_Text *n;
3924 c->o->have_ellipsis = 0;
3925 c->par = c->paragraphs = o->paragraphs;
3926 /* Go through all the text nodes to create the logical layout */
3927 EINA_INLIST_FOREACH(c->o->text_nodes, n)
3929 Evas_Object_Textblock_Node_Format *fnode;
3933 /* If it's not a new paragraph, either update it or skip it.
3934 * Remove all the paragraphs that were deleted */
3937 /* Remove all the deleted paragraphs at this point */
3938 while (c->par->text_node != n)
3940 Evas_Object_Textblock_Paragraph *tmp_par =
3941 (Evas_Object_Textblock_Paragraph *)
3942 EINA_INLIST_GET(c->par)->next;
3944 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
3945 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
3946 EINA_INLIST_GET(c->par));
3947 _paragraph_free(obj, c->par);
3952 /* If it's dirty, remove and recreate, if it's clean,
3953 * skip to the next. */
3956 Evas_Object_Textblock_Paragraph *prev_par = c->par;
3958 _layout_paragraph_new(c, n, EINA_TRUE);
3960 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
3961 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
3962 EINA_INLIST_GET(prev_par));
3963 _paragraph_free(obj, prev_par);
3967 c->par = (Evas_Object_Textblock_Paragraph *)
3968 EINA_INLIST_GET(c->par)->next;
3970 /* Update the format stack according to the node's
3972 fnode = n->format_node;
3973 while (fnode && (fnode->text_node == n))
3975 /* Only do this if this actually changes format */
3976 if (fnode->format_change)
3977 _layout_do_format(obj, c, &c->fmt, fnode,
3978 style_pad_l, style_pad_r,
3979 style_pad_t, style_pad_b, EINA_FALSE);
3980 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
3987 /* If it's a new paragraph, just add it. */
3988 _layout_paragraph_new(c, n, EINA_FALSE);
3992 _layout_update_bidi_props(c->o, c->par);
3995 /* For each text node to thorugh all of it's format nodes
3996 * append text from the start to the offset of the next format
3997 * using the last format got. if needed it also creates format
3998 * items this is the core algorithm of the layout mechanism.
3999 * Skip the unicode replacement chars when there are because
4000 * we don't want to print them. */
4001 fnode = n->format_node;
4003 while (fnode && (fnode->text_node == n))
4005 off += fnode->offset;
4006 /* No need to skip on the first run, or a non-visible one */
4007 _layout_text_append(c, c->fmt, n, start, off, o->repch);
4008 _layout_do_format(obj, c, &c->fmt, fnode, style_pad_l,
4009 style_pad_r, style_pad_t, style_pad_b, EINA_TRUE);
4010 if ((c->have_underline2) || (c->have_underline))
4012 if (*style_pad_b < c->underline_extend)
4013 *style_pad_b = c->underline_extend;
4014 c->have_underline = 0;
4015 c->have_underline2 = 0;
4016 c->underline_extend = 0;
4028 fnode->new = EINA_FALSE;
4029 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
4031 _layout_text_append(c, c->fmt, n, start, -1, o->repch);
4033 /* Clear the bidi props because we don't need them anymore. */
4034 if (c->par->bidi_props)
4036 evas_bidi_paragraph_props_unref(c->par->bidi_props);
4037 c->par->bidi_props = NULL;
4040 c->par = (Evas_Object_Textblock_Paragraph *)
4041 EINA_INLIST_GET(c->par)->next;
4044 /* Delete the rest of the layout paragraphs */
4047 Evas_Object_Textblock_Paragraph *tmp_par =
4048 (Evas_Object_Textblock_Paragraph *)
4049 EINA_INLIST_GET(c->par)->next;
4051 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4052 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
4053 EINA_INLIST_GET(c->par));
4054 _paragraph_free(obj, c->par);
4058 o->paragraphs = c->paragraphs;
4066 * Create the layout from the nodes.
4068 * @param obj the evas object - NOT NULL.
4069 * @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.
4070 * @param w the object's w, -1 means no wrapping (i.e infinite size)
4071 * @param h the object's h, -1 means inifinte size.
4072 * @param w_ret the object's calculated w.
4073 * @param h_ret the object's calculated h.
4076 _layout(const Evas_Object *obj, int w, int h, int *w_ret, int *h_ret)
4078 Evas_Object_Textblock *o;
4080 int style_pad_l = 0, style_pad_r = 0, style_pad_t = 0, style_pad_b = 0;
4083 o = (Evas_Object_Textblock *)(obj->object_data);
4085 c->obj = (Evas_Object *)obj;
4087 c->paragraphs = c->par = NULL;
4088 c->format_stack = NULL;
4093 c->wmax = c->hmax = 0;
4094 c->maxascent = c->maxdescent = 0;
4095 c->marginl = c->marginr = 0;
4096 c->have_underline = 0;
4097 c->have_underline2 = 0;
4098 c->underline_extend = 0;
4101 c->align_auto = EINA_TRUE;
4103 c->width_changed = (obj->cur.geometry.w != o->last_w);
4105 /* Start of logical layout creation */
4106 /* setup default base style */
4107 if ((c->o->style) && (c->o->style->default_tag))
4109 c->fmt = _layout_format_push(c, NULL, NULL);
4110 _format_fill(c->obj, c->fmt, c->o->style->default_tag);
4111 _format_finalize(c->obj, c->fmt);
4115 if (w_ret) *w_ret = 0;
4116 if (h_ret) *h_ret = 0;
4120 _layout_pre(c, &style_pad_l, &style_pad_r, &style_pad_t, &style_pad_b);
4121 c->paragraphs = o->paragraphs;
4123 /* If there are no paragraphs, create the minimum needed,
4124 * if the last paragraph has no lines/text, create that as well */
4127 _layout_paragraph_new(c, NULL, EINA_TRUE);
4128 o->paragraphs = c->paragraphs;
4130 c->par = (Evas_Object_Textblock_Paragraph *)
4131 EINA_INLIST_GET(c->paragraphs)->last;
4132 if (!c->par->logical_items)
4134 Evas_Object_Textblock_Text_Item *ti;
4135 ti = _layout_text_item_new(c, c->fmt);
4136 ti->parent.text_node = c->par->text_node;
4137 ti->parent.text_pos = 0;
4138 _layout_text_add_logical_item(c, ti, NULL);
4141 /* End of logical layout creation */
4143 /* Start of visual layout creation */
4145 Evas_Object_Textblock_Paragraph *last_vis_par = NULL;
4146 int par_index_step = o->num_paragraphs / TEXTBLOCK_PAR_INDEX_SIZE;
4147 int par_count = 1; /* Force it to take the first one */
4148 int par_index_pos = 0;
4150 if (par_index_step == 0) par_index_step = 1;
4152 /* Clear all of the index */
4153 memset(o->par_index, 0, sizeof(o->par_index));
4155 EINA_INLIST_FOREACH(c->paragraphs, c->par)
4157 _layout_update_par(c);
4159 /* Break if we should stop here. */
4162 last_vis_par = c->par;
4166 if ((par_index_pos < TEXTBLOCK_PAR_INDEX_SIZE) && (--par_count == 0))
4168 par_count = par_index_step;
4170 o->par_index[par_index_pos++] = c->par;
4174 /* Mark all the rest of the paragraphs as invisible */
4177 c->par = (Evas_Object_Textblock_Paragraph *)
4178 EINA_INLIST_GET(c->par)->next;
4181 c->par->visible = 0;
4182 c->par = (Evas_Object_Textblock_Paragraph *)
4183 EINA_INLIST_GET(c->par)->next;
4187 /* Get the last visible paragraph in the layout */
4188 if (!last_vis_par && c->paragraphs)
4189 last_vis_par = (Evas_Object_Textblock_Paragraph *)
4190 EINA_INLIST_GET(c->paragraphs)->last;
4193 c->hmax = last_vis_par->y + last_vis_par->h;
4196 /* Clean the rest of the format stack */
4197 while (c->format_stack)
4199 c->fmt = c->format_stack->data;
4200 c->format_stack = eina_list_remove_list(c->format_stack, c->format_stack);
4201 _format_unref_free(c->obj, c->fmt);
4204 if (w_ret) *w_ret = c->wmax;
4205 if (h_ret) *h_ret = c->hmax;
4207 /* Vertically align the textblock */
4208 if ((o->valign > 0.0) && (c->h > c->hmax))
4210 Evas_Coord adjustment = (c->h - c->hmax) * o->valign;
4211 Evas_Object_Textblock_Paragraph *par;
4212 EINA_INLIST_FOREACH(c->paragraphs, par)
4214 par->y += adjustment;
4218 if ((o->style_pad.l != style_pad_l) || (o->style_pad.r != style_pad_r) ||
4219 (o->style_pad.t != style_pad_t) || (o->style_pad.b != style_pad_b))
4221 o->style_pad.l = style_pad_l;
4222 o->style_pad.r = style_pad_r;
4223 o->style_pad.t = style_pad_t;
4224 o->style_pad.b = style_pad_b;
4225 _paragraphs_clear(obj, c->paragraphs);
4226 _layout(obj, w, h, w_ret, h_ret);
4232 * Relayout the object according to current object size.
4234 * @param obj the evas object - NOT NULL.
4237 _relayout(const Evas_Object *obj)
4239 Evas_Object_Textblock *o;
4241 o = (Evas_Object_Textblock *)(obj->object_data);
4242 _layout(obj, obj->cur.geometry.w, obj->cur.geometry.h,
4243 &o->formatted.w, &o->formatted.h);
4244 o->formatted.valid = 1;
4245 o->last_w = obj->cur.geometry.w;
4246 o->last_h = obj->cur.geometry.h;
4248 o->content_changed = 0;
4249 o->format_changed = EINA_FALSE;
4255 * Find the layout item and line that match the text node and position passed.
4257 * @param obj the evas object - NOT NULL.
4258 * @param n the text node - Not null.
4259 * @param pos the position to look for - valid.
4260 * @param[out] lnr the line found - not null.
4261 * @param[out] tir the item found - not null.
4262 * @see _find_layout_format_item_line_match()
4265 _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)
4267 Evas_Object_Textblock_Paragraph *found_par;
4268 Evas_Object_Textblock_Line *ln;
4269 Evas_Object_Textblock *o;
4271 o = (Evas_Object_Textblock *)(obj->object_data);
4272 if (!o->formatted.valid) _relayout(obj);
4277 _layout_paragraph_render(o, found_par);
4278 EINA_INLIST_FOREACH(found_par->lines, ln)
4280 Evas_Object_Textblock_Item *it;
4282 EINA_INLIST_FOREACH(ln->items, it)
4284 /* FIXME: p should be size_t, same goes for pos */
4285 int p = (int) it->text_pos;
4287 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
4289 Evas_Object_Textblock_Text_Item *ti =
4292 p += (int) ti->text_props.text_len;
4299 if (((pos >= (int) it->text_pos) && (pos < p)))
4317 * Return the line number 'line'.
4319 * @param obj the evas object - NOT NULL.
4320 * @param line the line to find
4321 * @return the line of line number or NULL if no line found.
4323 static Evas_Object_Textblock_Line *
4324 _find_layout_line_num(const Evas_Object *obj, int line)
4326 Evas_Object_Textblock_Paragraph *par;
4327 Evas_Object_Textblock_Line *ln;
4328 Evas_Object_Textblock *o;
4330 o = (Evas_Object_Textblock *)(obj->object_data);
4332 par = _layout_find_paragraph_by_line_no(o, line);
4335 _layout_paragraph_render(o, par);
4336 EINA_INLIST_FOREACH(par->lines, ln)
4338 if (par->line_no + ln->line_no == line) return ln;
4345 evas_object_textblock_add(Evas *e)
4349 MAGIC_CHECK(e, Evas, MAGIC_EVAS);
4352 obj = evas_object_new(e);
4353 evas_object_textblock_init(obj);
4354 evas_object_inject(obj, e);
4358 EAPI Evas_Textblock_Style *
4359 evas_textblock_style_new(void)
4361 Evas_Textblock_Style *ts;
4363 ts = calloc(1, sizeof(Evas_Textblock_Style));
4368 evas_textblock_style_free(Evas_Textblock_Style *ts)
4381 evas_textblock_style_set(Evas_Textblock_Style *ts, const char *text)
4387 /* If the style wasn't really changed, abort. */
4388 if ((!ts->style_text && !text) ||
4389 (ts->style_text && text && !strcmp(text, ts->style_text)))
4392 EINA_LIST_FOREACH(ts->objects, l, obj)
4394 Evas_Object_Textblock *o;
4396 o = (Evas_Object_Textblock *)(obj->object_data);
4397 _evas_textblock_invalidate_all(o);
4398 _evas_textblock_changed(o, obj);
4401 _style_replace(ts, text);
4405 // format MUST be KEY='VALUE'[KEY='VALUE']...
4407 const char *key_start, *key_stop, *val_start, *val_stop;
4409 key_start = key_stop = val_start = val_stop = NULL;
4420 if ((*p == '=') || (isspace(*p)))
4423 else if (!val_start)
4425 if (((*p) == '\'') && (*(p + 1)))
4430 if (((*p) == '\'') && (p > ts->style_text) && (p[-1] != '\\'))
4433 if ((key_start) && (key_stop) && (val_start) && (val_stop))
4435 char *tags, *replaces;
4436 Evas_Object_Style_Tag *tag;
4437 size_t tag_len = key_stop - key_start;
4438 size_t replace_len = val_stop - val_start;
4440 tags = malloc(tag_len + 1);
4443 memcpy(tags, key_start, tag_len);
4447 replaces = malloc(replace_len + 1);
4450 memcpy(replaces, val_start, replace_len);
4451 replaces[replace_len] = 0;
4453 if ((tags) && (replaces))
4455 if (!strcmp(tags, "DEFAULT"))
4457 ts->default_tag = replaces;
4462 tag = calloc(1, sizeof(Evas_Object_Style_Tag));
4466 tag->replace = replaces;
4467 tag->tag_len = tag_len;
4468 tag->replace_len = replace_len;
4469 ts->tags = (Evas_Object_Style_Tag *)eina_inlist_append(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
4480 if (tags) free(tags);
4481 if (replaces) free(replaces);
4483 key_start = key_stop = val_start = val_stop = NULL;
4491 evas_textblock_style_get(const Evas_Textblock_Style *ts)
4493 if (!ts) return NULL;
4494 return ts->style_text;
4497 /* textblock styles */
4499 evas_object_textblock_style_set(Evas_Object *obj, Evas_Textblock_Style *ts)
4502 if (ts == o->style) return;
4503 if ((ts) && (ts->delete_me)) return;
4506 Evas_Textblock_Style *old_ts;
4509 free(o->markup_text);
4510 o->markup_text = NULL;
4514 old_ts->objects = eina_list_remove(old_ts->objects, obj);
4515 if ((old_ts->delete_me) && (!old_ts->objects))
4516 evas_textblock_style_free(old_ts);
4520 ts->objects = eina_list_append(ts->objects, obj);
4524 _evas_textblock_invalidate_all(o);
4525 _evas_textblock_changed(o, obj);
4528 EAPI const Evas_Textblock_Style *
4529 evas_object_textblock_style_get(const Evas_Object *obj)
4531 TB_HEAD_RETURN(NULL);
4536 evas_object_textblock_replace_char_set(Evas_Object *obj, const char *ch)
4539 if (o->repch) eina_stringshare_del(o->repch);
4540 if (ch) o->repch = eina_stringshare_add(ch);
4541 else o->repch = NULL;
4542 _evas_textblock_invalidate_all(o);
4543 _evas_textblock_changed(o, obj);
4547 evas_object_textblock_legacy_newline_set(Evas_Object *obj, Eina_Bool mode)
4550 if (o->legacy_newline == mode)
4553 o->legacy_newline = mode;
4554 /* FIXME: Should recreate all the textnodes... For now, it's just
4555 * for new text inserted. */
4559 evas_object_textblock_legacy_newline_get(const Evas_Object *obj)
4561 TB_HEAD_RETURN(EINA_FALSE);
4562 return o->legacy_newline;
4566 evas_object_textblock_valign_set(Evas_Object *obj, double align)
4569 if (align < 0.0) align = 0.0;
4570 else if (align > 1.0) align = 1.0;
4571 if (o->valign == align) return;
4573 _evas_textblock_changed(o, obj);
4577 evas_object_textblock_valign_get(const Evas_Object *obj)
4579 TB_HEAD_RETURN(0.0);
4584 evas_object_textblock_bidi_delimiters_set(Evas_Object *obj, const char *delim)
4587 eina_stringshare_replace(&o->bidi_delimiters, delim);
4591 evas_object_textblock_bidi_delimiters_get(const Evas_Object *obj)
4593 TB_HEAD_RETURN(NULL);
4594 return o->bidi_delimiters;
4598 evas_object_textblock_replace_char_get(Evas_Object *obj)
4600 TB_HEAD_RETURN(NULL);
4606 * Advance p_buff to point after the end of the string. It's used with the
4607 * @ref escaped_strings[] variable.
4609 * @param p_buff the pointer to the current string.
4612 _escaped_advance_after_end_of_string(const char **p_buf)
4614 while (**p_buf != 0) (*p_buf)++;
4620 * Advance p_buff to point after the end of the string. It's used with the
4621 * @ref escaped_strings[] variable. Also chec if matches.
4624 * @param p_buff the pointer to the current string.
4627 _escaped_is_eq_and_advance(const char *s, const char *s_end,
4628 const char **p_m, const char *m_end)
4630 Eina_Bool reached_end;
4631 for (;((s < s_end) && (*p_m < m_end)); s++, (*p_m)++)
4635 _escaped_advance_after_end_of_string(p_m);
4640 reached_end = !**p_m;
4642 _escaped_advance_after_end_of_string(p_m);
4644 return ((s == s_end) && reached_end);
4650 * @param s the string to match
4652 static inline const char *
4653 _escaped_char_match(const char *s, int *adv)
4655 const char *map_itr, *map_end, *mc, *sc;
4657 map_itr = escape_strings;
4658 map_end = map_itr + sizeof(escape_strings);
4660 while (map_itr < map_end)
4666 _escaped_advance_after_end_of_string(&map_itr);
4667 if (map_itr >= map_end) break;
4672 while ((*mc) && (*sc))
4674 if ((unsigned char)*sc < (unsigned char)*mc) return NULL;
4675 if (*sc != *mc) match = 0;
4681 *adv = mc - map_itr;
4684 _escaped_advance_after_end_of_string(&map_itr);
4693 * @param s the string to match
4695 static inline const char *
4696 _escaped_char_get(const char *s, const char *s_end)
4698 /* Handle numeric escape codes. */
4701 static char utf8_escape[7]; /* Support up to 6 bytes utf8 */
4703 Eina_Unicode uchar[2] = { 0, 0 };
4707 s += 2; /* Skip "&#" */
4709 if (tolower(*s) == 'x')
4716 if (len >= sizeof(ustr) + 1)
4719 memcpy(ustr, s, len);
4721 uchar[0] = strtol(ustr, NULL, base);
4726 utf8_char = eina_unicode_unicode_to_utf8(uchar, NULL);
4727 strcpy(utf8_escape, utf8_char);
4734 const char *map_itr, *map_end;
4736 map_itr = escape_strings;
4737 map_end = map_itr + sizeof(escape_strings);
4739 while (map_itr < map_end)
4741 if (_escaped_is_eq_and_advance(s, s_end, &map_itr, map_end))
4743 if (map_itr < map_end)
4744 _escaped_advance_after_end_of_string(&map_itr);
4752 evas_textblock_escape_string_get(const char *escape)
4755 return _escaped_char_get(escape, escape + strlen(escape));
4759 evas_textblock_escape_string_range_get(const char *escape_start, const char *escape_end)
4761 return _escaped_char_get(escape_start, escape_end);
4765 evas_textblock_string_escape_get(const char *string, int *len_ret)
4768 return _escaped_char_match(string, len_ret);
4773 * Appends the escaped char beteewn s and s_end to the curosr
4776 * @param s the start of the string
4777 * @param s_end the end of the string.
4780 _append_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
4785 escape = _escaped_char_get(s, s_end);
4787 evas_textblock_cursor_text_append(cur, escape);
4792 * prepends the escaped char beteewn s and s_end to the curosr
4795 * @param s the start of the string
4796 * @param s_end the end of the string.
4799 _prepend_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
4804 escape = _escaped_char_get(s, s_end);
4806 evas_textblock_cursor_text_prepend(cur, escape);
4811 evas_object_textblock_text_markup_set(Evas_Object *obj, const char *text)
4814 if ((text != o->markup_text) && (o->markup_text))
4816 free(o->markup_text);
4817 o->markup_text = NULL;
4822 if (text != o->markup_text)
4824 if (text) o->markup_text = strdup(text);
4828 evas_textblock_cursor_paragraph_first(o->cursor);
4830 evas_object_textblock_text_markup_prepend(o->cursor, text);
4831 /* Point all the cursors to the starrt */
4834 Evas_Textblock_Cursor *data;
4836 evas_textblock_cursor_paragraph_first(o->cursor);
4837 EINA_LIST_FOREACH(o->cursors, l, data)
4838 evas_textblock_cursor_paragraph_first(data);
4843 evas_object_textblock_text_markup_prepend(Evas_Textblock_Cursor *cur, const char *text)
4845 Evas_Object *obj = cur->obj;
4850 char *tag_start, *tag_end, *esc_start, *esc_end;
4852 tag_start = tag_end = esc_start = esc_end = NULL;
4855 /* This loop goes through all of the mark up text until it finds format
4856 * tags, escape sequences or the terminating NULL. When it finds either
4857 * of those, it appends the text found up until that point to the textblock
4858 * proccesses whatever found. It repeats itself until the termainating
4859 * NULL is reached. */
4862 /* If we got to the end of string or just finished/started tag
4863 * or escape sequence handling. */
4865 (tag_end) || (esc_end) ||
4866 (tag_start) || (esc_start))
4870 /* If we reached to a tag ending, analyze the tag */
4872 size_t ttag_len = tag_end - tag_start;
4875 ttag = malloc(ttag_len + 1);
4878 memcpy(ttag, tag_start, ttag_len);
4880 evas_textblock_cursor_format_prepend(cur, ttag);
4883 tag_start = tag_end = NULL;
4887 _prepend_escaped_char(cur, esc_start, esc_end + 1);
4888 esc_start = esc_end = NULL;
4892 _prepend_text_run(cur, s, p);
4902 /* Append the text prior to this to the textblock and mark
4903 * the start of the tag */
4906 _prepend_text_run(cur, s, p);
4922 /* Append the text prior to this to the textblock and mark
4923 * the start of the escape sequence */
4926 _prepend_text_run(cur, s, p);
4938 /* Unicode object replcament char */
4939 else if (!strncmp("\xEF\xBF\xBC", p, 3))
4941 /*FIXME: currently just remove them, maybe do something
4942 * fancier in the future, atm it breaks if this char
4944 _prepend_text_run(cur, s, p);
4945 p += 2; /* it's also advanced later in this loop need +3
4947 s = p + 1; /* One after the end of the replacement char */
4952 _evas_textblock_changed(o, obj);
4958 * An helper function to markup get. Appends the format from fnode to the strbugf txt.
4960 * @param o the textblock object.
4961 * @param txt the strbuf to append to.
4962 * @param fnode the format node to process.
4965 _markup_get_format_append(Evas_Object_Textblock *o __UNUSED__, Eina_Strbuf *txt, Evas_Object_Textblock_Node_Format *fnode)
4967 eina_strbuf_append_char(txt, '<');
4972 // FIXME: need to escape
4973 s = fnode->orig_format;
4974 if (*s == '-') pop = 1;
4975 while ((*s == ' ') || (*s == '+') || (*s == '-')) s++;
4976 if (pop) eina_strbuf_append_char(txt, '/');
4977 eina_strbuf_append(txt, s);
4979 eina_strbuf_append_char(txt, '>');
4984 * An helper function to markup get. Appends the text in text.
4986 * @param txt the strbuf to append to.
4987 * @param text the text to process.
4990 _markup_get_text_append(Eina_Strbuf *txt, const Eina_Unicode *text)
4992 char *p = eina_unicode_unicode_to_utf8(text, NULL);
4999 escape = _escaped_char_match(p, &adv);
5003 eina_strbuf_append(txt, escape);
5007 eina_strbuf_append_char(txt, *p);
5014 evas_object_textblock_text_markup_get(const Evas_Object *obj)
5016 Evas_Object_Textblock_Node_Text *n;
5017 Eina_Strbuf *txt = NULL;
5019 TB_HEAD_RETURN(NULL);
5020 if (o->markup_text) return(o->markup_text);
5021 txt = eina_strbuf_new();
5022 EINA_INLIST_FOREACH(o->text_nodes, n)
5024 Evas_Object_Textblock_Node_Format *fnode;
5025 Eina_Unicode *text_base, *text;
5028 /* For each text node to thorugh all of it's format nodes
5029 * append text from the start to the offset of the next format
5030 * using the last format got. if needed it also creates format items
5031 * this is the core algorithm of the layout mechanism.
5032 * Skip the unicode replacement chars when there are because
5033 * we don't want to print them. */
5035 eina_unicode_strndup(eina_ustrbuf_string_get(n->unicode),
5036 eina_ustrbuf_length_get(n->unicode));
5037 fnode = n->format_node;
5039 while (fnode && (fnode->text_node == n))
5041 Eina_Unicode tmp_ch;
5042 off += fnode->offset;
5043 /* No need to skip on the first run */
5045 text[off] = 0; /* Null terminate the part of the string */
5046 _markup_get_text_append(txt, text);
5047 _markup_get_format_append(o, txt, fnode);
5048 text[off] = tmp_ch; /* Restore the char */
5059 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
5061 /* Add the rest, skip replacement */
5062 _markup_get_text_append(txt, text);
5067 o->markup_text = eina_strbuf_string_steal(txt);
5068 eina_strbuf_free(txt);
5069 return o->markup_text;
5076 * Merge the current node with the next, no need to remove PS, already
5079 * @param o the text block object.
5080 * @param to merge into to.
5083 _evas_textblock_nodes_merge(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *to)
5085 Evas_Object_Textblock_Node_Format *itr;
5086 Evas_Object_Textblock_Node_Format *pnode;
5087 Evas_Object_Textblock_Node_Text *from;
5088 const Eina_Unicode *text;
5092 from = _NODE_TEXT(EINA_INLIST_GET(to)->next);
5094 to_len = eina_ustrbuf_length_get(to->unicode);
5095 text = eina_ustrbuf_string_get(from->unicode);
5096 len = eina_ustrbuf_length_get(from->unicode);
5097 eina_ustrbuf_append_length(to->unicode, text, len);
5099 itr = from->format_node;
5100 if (itr && (itr->text_node == from))
5102 pnode = _NODE_FORMAT(EINA_INLIST_GET(itr)->prev);
5103 if (pnode && (pnode->text_node == to))
5105 itr->offset += to_len - _evas_textblock_node_format_pos_get(pnode);
5109 itr->offset += to_len;
5113 while (itr && (itr->text_node == from))
5115 itr->text_node = to;
5116 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
5118 if (!to->format_node || (to->format_node->text_node != to))
5120 to->format_node = from->format_node;
5123 /* When it comes to how we handle it, merging is like removing both nodes
5124 * and creating a new one, se we need to do the needed cleanups. */
5126 to->par->text_node = NULL;
5129 to->new = EINA_TRUE;
5131 _evas_textblock_cursors_set_node(o, from, to);
5132 _evas_textblock_node_text_remove(o, from);
5137 * Merge the current node with the next, no need to remove PS, already
5140 * @param cur the cursor that points to the current node
5143 _evas_textblock_cursor_nodes_merge(Evas_Textblock_Cursor *cur)
5145 Evas_Object_Textblock_Node_Text *nnode;
5146 Evas_Object_Textblock *o;
5150 len = eina_ustrbuf_length_get(cur->node->unicode);
5152 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5153 nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
5154 _evas_textblock_nodes_merge(o, cur->node);
5155 _evas_textblock_cursors_update_offset(cur, nnode, 0, len);
5156 _evas_textblock_cursors_set_node(o, nnode, cur->node);
5157 if (nnode == o->cursor->node)
5159 o->cursor->node = cur->node;
5160 o->cursor->pos += len;
5166 * Return the format at a specific position.
5168 * @param cur the cursor to the position.
5169 * @return the format node at the specific position or NULL if not found.
5171 static Evas_Object_Textblock_Node_Format *
5172 _evas_textblock_cursor_node_format_at_pos_get(const Evas_Textblock_Cursor *cur)
5174 Evas_Object_Textblock_Node_Format *node;
5175 Evas_Object_Textblock_Node_Format *itr;
5178 if (!cur->node) return NULL;
5180 node = cur->node->format_node;
5181 if (!node) return NULL;
5182 /* If there is no exclusive format node to this paragraph return the
5183 * previous's node */
5184 /* Find the main format node */
5185 EINA_INLIST_FOREACH(node, itr)
5187 if (itr->text_node != cur->node)
5191 if ((position + itr->offset) == cur->pos)
5195 position += itr->offset;
5202 * Return the last format node at the position of the format node n.
5204 * @param n a format node at the position.
5205 * @return the last format node at the position of n.
5207 static Evas_Object_Textblock_Node_Format *
5208 _evas_textblock_node_format_last_at_off(const Evas_Object_Textblock_Node_Format *n)
5210 const Evas_Object_Textblock_Node_Format *nnode;
5211 const Evas_Object_Textblock_Node_Text *tnode;
5212 if (!n) return NULL;
5214 tnode = n->text_node;
5218 nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
5220 while (nnode && (nnode->text_node == tnode) && (nnode->offset == 0));
5222 return (Evas_Object_Textblock_Node_Format *) n;
5227 * Returns the visible format at a specific location.
5229 * @param n a format at the specific position.
5230 * @return the format node at the specific position or NULL if not found.
5232 static Evas_Object_Textblock_Node_Format *
5233 _evas_textblock_node_visible_at_pos_get(const Evas_Object_Textblock_Node_Format *n)
5235 const Evas_Object_Textblock_Node_Format *nnode;
5236 if (!n) return NULL;
5237 /* The visible format is the last one, because it inserts a replacement
5238 * char that advances the next formats. */
5244 if (n->visible) return (Evas_Object_Textblock_Node_Format *) n;
5245 nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
5247 while (nnode && (nnode->offset == 0));
5254 * Return the last format that applies to a specific cursor or at the specific
5255 * position the cursor points to. This means either a cursor at or before the
5256 * position of the cursor in the text node is returned or the previous's text
5257 * node's format node.
5259 * @param cur the position to look at.
5260 * @return the format node found.
5262 static Evas_Object_Textblock_Node_Format *
5263 _evas_textblock_cursor_node_format_before_or_at_pos_get(const Evas_Textblock_Cursor *cur)
5265 Evas_Object_Textblock_Node_Format *node, *pitr = NULL;
5266 Evas_Object_Textblock_Node_Format *itr;
5267 size_t position = 0;
5269 if (!cur->node) return NULL;
5271 node = cur->node->format_node;
5272 if (!node) return NULL;
5273 /* If there is no exclusive format node to this paragraph return the
5274 * previous's node */
5275 if (node->text_node != cur->node)
5279 else if (node->offset > cur->pos)
5281 return _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5283 /* Find the main format node */
5284 pitr = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5285 EINA_INLIST_FOREACH(node, itr)
5287 if ((itr->text_node != cur->node) ||
5288 ((position + itr->offset) > cur->pos))
5292 else if ((position + itr->offset) == cur->pos)
5297 position += itr->offset;
5304 * Find the layout item and line that match the cursor.
5306 * @param cur the cursor we are currently at. - NOT NULL.
5307 * @param[out] lnr the line found - not null.
5308 * @param[out] itr the item found - not null.
5309 * @return EINA_TRUE if we matched the previous format, EINA_FALSE otherwise.
5312 _find_layout_item_match(const Evas_Textblock_Cursor *cur, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Item **itr)
5314 Evas_Textblock_Cursor cur2;
5315 Eina_Bool previous_format = EINA_FALSE;
5317 cur2.obj = cur->obj;
5318 evas_textblock_cursor_copy(cur, &cur2);
5324 if (_evas_textblock_cursor_is_at_the_end(cur) &&
5325 evas_textblock_cursor_format_is_visible_get(&cur2))
5327 _find_layout_item_line_match(cur2.obj, cur2.node, cur2.pos, lnr, itr);
5328 previous_format = EINA_TRUE;
5332 _find_layout_item_line_match(cur->obj, cur->node, cur->pos, lnr, itr);
5334 return previous_format;
5337 EAPI Evas_Textblock_Cursor *
5338 evas_object_textblock_cursor_get(const Evas_Object *obj)
5340 TB_HEAD_RETURN(NULL);
5344 EAPI Evas_Textblock_Cursor *
5345 evas_object_textblock_cursor_new(const Evas_Object *obj)
5347 Evas_Textblock_Cursor *cur;
5349 TB_HEAD_RETURN(NULL);
5350 cur = calloc(1, sizeof(Evas_Textblock_Cursor));
5351 cur->obj = (Evas_Object *) obj;
5352 cur->node = o->text_nodes;
5355 o->cursors = eina_list_append(o->cursors, cur);
5360 evas_textblock_cursor_free(Evas_Textblock_Cursor *cur)
5362 Evas_Object_Textblock *o;
5365 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5366 if (cur == o->cursor) return;
5367 o->cursors = eina_list_remove(o->cursors, cur);
5372 evas_textblock_cursor_is_format(const Evas_Textblock_Cursor *cur)
5374 if (!cur || !cur->node) return EINA_FALSE;
5375 if (evas_textblock_cursor_format_is_visible_get(cur)) return EINA_TRUE;
5376 return (_evas_textblock_cursor_node_format_at_pos_get(cur)) ?
5377 EINA_TRUE : EINA_FALSE;
5380 EAPI const Eina_List *
5381 evas_textblock_node_format_list_get(const Evas_Object *obj, const char *anchor)
5383 TB_HEAD_RETURN(NULL);
5384 if (!strcmp(anchor, "a"))
5385 return o->anchors_a;
5386 else if (!strcmp(anchor, "item"))
5387 return o->anchors_item;
5392 EAPI const Evas_Object_Textblock_Node_Format *
5393 evas_textblock_node_format_first_get(const Evas_Object *obj)
5395 TB_HEAD_RETURN(NULL);
5396 return o->format_nodes;
5399 EAPI const Evas_Object_Textblock_Node_Format *
5400 evas_textblock_node_format_last_get(const Evas_Object *obj)
5402 TB_HEAD_RETURN(NULL);
5403 if (o->format_nodes)
5405 return _NODE_FORMAT(EINA_INLIST_GET(o->format_nodes)->last);
5410 EAPI const Evas_Object_Textblock_Node_Format *
5411 evas_textblock_node_format_next_get(const Evas_Object_Textblock_Node_Format *n)
5413 return _NODE_FORMAT(EINA_INLIST_GET(n)->next);
5416 EAPI const Evas_Object_Textblock_Node_Format *
5417 evas_textblock_node_format_prev_get(const Evas_Object_Textblock_Node_Format *n)
5419 return _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
5423 evas_textblock_node_format_remove_pair(Evas_Object *obj,
5424 Evas_Object_Textblock_Node_Format *n)
5426 Evas_Object_Textblock_Node_Text *tnode1;
5427 Evas_Object_Textblock_Node_Format *fmt, *found_node = NULL;
5428 Eina_List *fstack = NULL;
5437 const char *fstr = fmt->orig_format;
5439 if (fstr && (*fstr == '+'))
5441 fstack = eina_list_prepend(fstack, fmt);
5443 else if (fstr && (*fstr == '-'))
5448 fstr_len = strlen(fstr);
5449 /* Generic popper, just pop */
5450 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
5452 fstack = eina_list_remove_list(fstack, fstack);
5459 /* Find the matching format and pop it, if the matching format
5460 * is out format, i.e the last one, pop and break. */
5464 Evas_Object_Textblock_Node_Format *fnode;
5465 EINA_LIST_FOREACH(fstack, i, fnode)
5467 if (_FORMAT_IS_CLOSER_OF(
5468 fnode->orig_format, fstr, fstr_len))
5470 /* Last one, this is our item! */
5471 if (!eina_list_next(i))
5476 fstack = eina_list_remove_list(fstack, i);
5483 fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
5485 while (fmt && fstack);
5489 fstack = eina_list_free(fstack);
5493 size_t ind = _evas_textblock_node_format_pos_get(n);
5494 const char *format = n->format;
5495 Evas_Textblock_Cursor cur;
5498 eina_ustrbuf_remove(n->text_node->unicode, ind, ind + 1);
5499 if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
5501 evas_textblock_cursor_at_format_set(&cur, n);
5502 _evas_textblock_cursor_nodes_merge(&cur);
5504 _evas_textblock_cursors_update_offset(&cur, n->text_node, ind, -1);
5506 tnode1 = n->text_node;
5507 _evas_textblock_node_format_remove(o, n, 0);
5508 if (found_node && (found_node != n))
5510 Evas_Object_Textblock_Node_Text *tnode2;
5511 tnode2 = found_node->text_node;
5512 /* found_node can never be visible! (it's the closing format) */
5513 _evas_textblock_node_format_remove(o, found_node, 0);
5515 /* FIXME: Should be unified in the layout, for example, added to a list
5516 * that checks this kind of removals. But until then, this is very fast
5518 /* Mark all the text nodes in between the removed formats as dirty. */
5521 tnode1->dirty = EINA_TRUE;
5522 if (tnode1 == tnode2)
5525 _NODE_TEXT(EINA_INLIST_GET(tnode1)->next);
5529 _evas_textblock_changed(o, obj);
5533 evas_textblock_cursor_paragraph_first(Evas_Textblock_Cursor *cur)
5535 Evas_Object_Textblock *o;
5537 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5538 cur->node = o->text_nodes;
5544 evas_textblock_cursor_paragraph_last(Evas_Textblock_Cursor *cur)
5546 Evas_Object_Textblock *o;
5547 Evas_Object_Textblock_Node_Text *node;
5550 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5551 node = o->text_nodes;
5554 node = _NODE_TEXT(EINA_INLIST_GET(node)->last);
5558 evas_textblock_cursor_paragraph_char_last(cur);
5569 evas_textblock_cursor_paragraph_next(Evas_Textblock_Cursor *cur)
5571 if (!cur) return EINA_FALSE;
5572 if (!cur->node) return EINA_FALSE;
5573 /* If there is a current text node, return the next text node (if exists)
5574 * otherwise, just return False. */
5577 Evas_Object_Textblock_Node_Text *nnode;
5578 nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
5591 evas_textblock_cursor_paragraph_prev(Evas_Textblock_Cursor *cur)
5593 Evas_Object_Textblock_Node_Text *node;
5594 if (!cur) return EINA_FALSE;
5595 if (!cur->node) return EINA_FALSE;
5596 /* If the current node is a text node, just get the prev if any,
5597 * if it's a format, get the current text node out of the format and return
5598 * the prev text node if any. */
5600 /* If there is a current text node, return the prev text node
5601 * (if exists) otherwise, just return False. */
5604 Evas_Object_Textblock_Node_Text *pnode;
5605 pnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->prev);
5609 evas_textblock_cursor_paragraph_char_last(cur);
5617 evas_textblock_cursor_set_at_format(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *n)
5619 evas_textblock_cursor_at_format_set(cur, n);
5623 evas_textblock_cursor_format_next(Evas_Textblock_Cursor *cur)
5625 Evas_Object_Textblock_Node_Format *node;
5627 if (!cur) return EINA_FALSE;
5628 if (!cur->node) return EINA_FALSE;
5629 /* If the current node is a format node, just get the next if any,
5630 * if it's a text, get the current format node out of the text and return
5631 * the next format node if any. */
5632 node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
5633 node = _evas_textblock_node_format_last_at_off(node);
5636 if (cur->node->format_node)
5638 cur->pos = _evas_textblock_node_format_pos_get(node);
5642 /* If there is a current text node, return the next format node (if exists)
5643 * otherwise, just return False. */
5646 Evas_Object_Textblock_Node_Format *nnode;
5647 nnode = _NODE_FORMAT(EINA_INLIST_GET(node)->next);
5650 cur->node = nnode->text_node;
5651 cur->pos = _evas_textblock_node_format_pos_get(nnode);
5660 evas_textblock_cursor_format_prev(Evas_Textblock_Cursor *cur)
5662 const Evas_Object_Textblock_Node_Format *node;
5663 if (!cur) return EINA_FALSE;
5664 if (!cur->node) return EINA_FALSE;
5665 node = evas_textblock_cursor_format_get(cur);
5668 node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
5671 cur->node = node->text_node;
5672 cur->pos = _evas_textblock_node_format_pos_get(node);
5677 /* If there is a current text node, return the next text node (if exists)
5678 * otherwise, just return False. */
5681 Evas_Object_Textblock_Node_Format *pnode;
5682 pnode = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5685 cur->node = pnode->text_node;
5686 cur->pos = _evas_textblock_node_format_pos_get(pnode);
5695 evas_textblock_cursor_char_next(Evas_Textblock_Cursor *cur)
5698 const Eina_Unicode *text;
5700 if (!cur) return EINA_FALSE;
5701 if (!cur->node) return EINA_FALSE;
5704 text = eina_ustrbuf_string_get(cur->node->unicode);
5705 if (text[ind]) ind++;
5706 /* Only allow pointing a null if it's the last paragraph.
5707 * because we don't have a PS there. */
5715 if (!evas_textblock_cursor_paragraph_next(cur))
5717 /* If we already were at the end, that means we don't have
5718 * where to go next we should return FALSE */
5719 if (cur->pos == (size_t) ind)
5733 evas_textblock_cursor_char_prev(Evas_Textblock_Cursor *cur)
5735 if (!cur) return EINA_FALSE;
5736 if (!cur->node) return EINA_FALSE;
5743 return evas_textblock_cursor_paragraph_prev(cur);
5747 evas_textblock_cursor_paragraph_char_first(Evas_Textblock_Cursor *cur)
5755 evas_textblock_cursor_paragraph_char_last(Evas_Textblock_Cursor *cur)
5760 if (!cur->node) return;
5761 ind = eina_ustrbuf_length_get(cur->node->unicode);
5762 /* If it's not the last paragraph, go back one, because we want to point
5763 * to the PS, not the NULL */
5764 if (EINA_INLIST_GET(cur->node)->next)
5775 evas_textblock_cursor_line_char_first(Evas_Textblock_Cursor *cur)
5777 Evas_Object_Textblock *o;
5778 Evas_Object_Textblock_Line *ln = NULL;
5779 Evas_Object_Textblock_Item *it = NULL;
5782 if (!cur->node) return;
5783 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5784 if (!o->formatted.valid) _relayout(cur->obj);
5786 _find_layout_item_match(cur, &ln, &it);
5791 Evas_Object_Textblock_Item *i;
5793 EINA_INLIST_FOREACH(ln->items, i)
5795 if (it->text_pos > i->text_pos)
5803 cur->pos = it->text_pos;
5804 cur->node = it->text_node;
5809 evas_textblock_cursor_line_char_last(Evas_Textblock_Cursor *cur)
5811 Evas_Object_Textblock *o;
5812 Evas_Object_Textblock_Line *ln = NULL;
5813 Evas_Object_Textblock_Item *it = NULL;
5816 if (!cur->node) return;
5817 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5818 if (!o->formatted.valid) _relayout(cur->obj);
5820 _find_layout_item_match(cur, &ln, &it);
5825 Evas_Object_Textblock_Item *i;
5827 EINA_INLIST_FOREACH(ln->items, i)
5829 if (it->text_pos < i->text_pos)
5839 cur->node = it->text_node;
5840 cur->pos = it->text_pos;
5841 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
5843 ind = _ITEM_TEXT(it)->text_props.text_len - 1;
5844 if (!IS_AT_END(_ITEM_TEXT(it), ind)) ind++;
5847 else if (!EINA_INLIST_GET(ln)->next && !EINA_INLIST_GET(ln->par)->next)
5856 * checks if a format (as a string) is visible/changes format and sets the
5857 * fnode properties accordingly.
5859 * @param fnode the format node
5860 * @param s the string.
5863 _evas_textblock_format_is_visible(Evas_Object_Textblock_Node_Format *fnode,
5867 Eina_Bool is_opener = EINA_TRUE;
5869 fnode->visible = fnode->format_change = EINA_FALSE;
5870 fnode->anchor = ANCHOR_NONE;
5873 if (s[0] == '+' || s[0] == '-')
5875 is_opener = (s[0] == '+');
5877 fnode->format_change = EINA_TRUE;
5880 while ((item = _format_parse(&s)))
5882 int itlen = s - item;
5883 /* We care about all of the formats even after a - except for
5884 * item which we don't care after a - because it's just a standard
5886 if ((!strncmp(item, "\n", itlen) || !strncmp(item, "\\n", itlen)) ||
5887 (!strncmp(item, "\t", itlen) || !strncmp(item, "\\t", itlen)) ||
5888 (!strncmp(item, "ps", itlen) && (itlen >= 2)) ||
5889 (!strncmp(item, "item", itlen) && (itlen >= 4) && is_opener))
5891 fnode->visible = EINA_TRUE;
5894 if (is_opener && !strncmp(item, "a", itlen))
5896 fnode->anchor = ANCHOR_A;
5898 else if (is_opener && !strncmp(item, "item", itlen) && (itlen >= 4))
5900 fnode->anchor = ANCHOR_ITEM;
5906 * Sets the cursor to the position of where the fmt points to.
5908 * @param cur the cursor to update.
5909 * @param fmt the format to set according to.
5912 static void __UNUSED__
5913 _evas_textblock_cursor_node_text_at_format(Evas_Textblock_Cursor *cur, Evas_Object_Textblock_Node_Format *fmt)
5915 Evas_Object_Textblock_Node_Text *text;
5916 Evas_Object_Textblock_Node_Format *base_format;
5917 Evas_Object_Textblock_Node_Format *itr;
5918 size_t position = 0;
5920 if (!cur || !fmt) return;
5921 /* Find the main format node */
5922 text = fmt->text_node;
5924 base_format = text->format_node;
5925 EINA_INLIST_FOREACH(base_format, itr)
5931 position += itr->offset;
5933 cur->pos = position;
5940 * Remove pairs of + and - formats and also remove formats without + or -
5941 * i.e formats that pair to themselves. Only removes invisible formats
5942 * that pair themselves, if you want to remove invisible formats that pair
5943 * themselves, please first change fmt->visible to EINA_FALSE.
5945 * @param o the textblock object.
5946 * @param fmt the current format.
5949 _evas_textblock_node_format_remove_matching(Evas_Object_Textblock *o,
5950 Evas_Object_Textblock_Node_Format *fmt)
5952 Evas_Object_Textblock_Node_Text *tnode;
5953 Eina_List *formats = NULL;
5958 tnode = fmt->text_node;
5962 Evas_Object_Textblock_Node_Format *nnode;
5963 const char *fstr = fmt->orig_format;
5965 nnode = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
5968 offset = nnode->offset;
5972 if (fstr && (*fstr == '+'))
5974 formats = eina_list_prepend(formats, fmt);
5976 else if (fstr && (*fstr == '-'))
5978 Evas_Object_Textblock_Node_Format *fnode;
5982 fstr_len = strlen(fstr);
5983 /* Generic popper, just pop */
5984 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
5986 fnode = eina_list_data_get(formats);
5987 formats = eina_list_remove_list(formats, formats);
5988 _evas_textblock_node_format_remove(o, fnode, 0);
5989 _evas_textblock_node_format_remove(o, fmt, 0);
5991 /* Find the matching format and pop it, if the matching format
5992 * is our format, i.e the last one, pop and break. */
5995 Eina_List *i, *next;
5996 EINA_LIST_FOREACH_SAFE(formats, i, next, fnode)
5998 if (_FORMAT_IS_CLOSER_OF(
5999 fnode->orig_format, fstr, fstr_len))
6001 fnode = eina_list_data_get(i);
6002 formats = eina_list_remove_list(formats, i);
6003 _evas_textblock_node_format_remove(o, fnode, 0);
6004 _evas_textblock_node_format_remove(o, fmt, 0);
6010 else if (!fmt->visible)
6012 _evas_textblock_node_format_remove(o, fmt, 0);
6016 while (fmt && (offset == 0) && (fmt->text_node == tnode));
6017 eina_list_free(formats);
6021 * Add the offset (may be negative) to the first node after fmt which is
6022 * pointing to the text node tnode or to o->format_nodes if fmt is null
6023 * and it points to tnode.
6025 * @param o the textblock object.
6026 * @param tnode the text node the format should point to.
6027 * @param fmt the current format.
6028 * @param offset the offest to add (may be negative).
6031 _evas_textblock_node_format_adjust_offset(Evas_Object_Textblock *o,
6032 Evas_Object_Textblock_Node_Text *tnode,
6033 Evas_Object_Textblock_Node_Format *fmt, int offset)
6037 fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
6041 fmt = o->format_nodes;
6043 if (fmt && (tnode == fmt->text_node))
6045 fmt->offset += offset;
6051 * Removes a format node updating the offset of the next format node and the
6052 * text nodes pointing to this node.
6054 * @param o the textblock object.
6055 * @param n the fromat node to remove
6058 _evas_textblock_node_format_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n, int visible_adjustment)
6060 /* Update the text nodes about the change */
6062 Evas_Object_Textblock_Node_Format *nnode;
6063 nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->next);
6064 /* If there's a next node that belongs to the same text node
6065 * and the curret node was the main one, advance the format node */
6066 if (nnode && (nnode->text_node == n->text_node))
6068 if (nnode->text_node->format_node == n)
6070 nnode->text_node->format_node = nnode;
6075 Evas_Object_Textblock_Node_Text *tnode;
6076 /* If there's no next one update the text nodes */
6077 nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
6078 tnode = n->text_node;
6079 /* Even if it's not the current text_node's main node
6080 * it can still be the next's. */
6081 if (tnode && (tnode->format_node != n))
6083 tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
6085 while (tnode && (tnode->format_node == n))
6087 tnode->format_node = nnode;
6088 tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
6092 _evas_textblock_node_format_adjust_offset(o, n->text_node, n,
6093 n->offset - visible_adjustment);
6095 o->format_nodes = _NODE_FORMAT(eina_inlist_remove(
6096 EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n)));
6097 _evas_textblock_node_format_free(o, n);
6102 * Sets all the offsets of the format nodes between start and end in the text
6103 * node n to 0 and sets visibility to EINA_FALSE.
6104 * If end == -1 end means the end of the string.
6105 * Assumes there is a prev node or the current node will be preserved.
6107 * @param n the text node the positinos refer to.
6108 * @param start the start of where to delete from.
6109 * @param end the end of the section to delete, if end == -1 it means the end of the string.
6110 * @returns #EINA_TRUE if removed a PS, false otherwise.
6113 _evas_textblock_node_text_adjust_offsets_to_start(Evas_Object_Textblock *o,
6114 Evas_Object_Textblock_Node_Text *n, size_t start, int end)
6116 Evas_Object_Textblock_Node_Format *last_node, *itr;
6117 Evas_Object_Textblock_Node_Text *new_node;
6121 int update_format_node;
6125 itr = n->format_node;
6126 if (!itr || (itr->text_node != n)) return EINA_FALSE;
6129 if ((end < 0) || ((size_t) end == eina_ustrbuf_length_get(n->unicode)))
6135 /* We don't want the last one */
6139 /* If we are not removing the text node, all should stay in this text
6140 * node, otherwise, everything should move to the previous node */
6141 if ((start == 0) && !use_end)
6143 new_node = _NODE_TEXT(EINA_INLIST_GET(n)->prev);
6154 /* Find the first node after start */
6155 while (itr && (itr->text_node == n))
6162 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6165 if (!itr || (itr->text_node != n))
6170 update_format_node = ((itr == n->format_node) && (new_node != n));
6171 delta = orig_end - pos;
6172 itr->offset -= pos - start;
6174 while (itr && (itr->text_node == n))
6177 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6181 pos += last_node->offset;
6184 /* start is negative when this gets relevant */
6185 if (use_end && (pos > (size_t) end))
6187 last_node->offset -= delta;
6191 delta = orig_end - pos;
6194 last_node->offset = 0;
6200 last_node->visible = EINA_FALSE;
6202 if (!itr || (itr && (itr->text_node != n)))
6204 /* Remove the PS, and return since it's the end of the node */
6205 if (_IS_PARAGRAPH_SEPARATOR(o, last_node->format))
6207 _evas_textblock_node_format_remove(o, last_node, 0);
6212 last_node->text_node = new_node;
6213 if (update_format_node)
6215 n->format_node = last_node;
6224 * Removes all the format nodes between start and end in the text node n.
6225 * This function updates the offset of the next format node and the
6226 * text nodes pointing to it. if end == -1 end means the end of the string.
6228 * @param o the textblock object.
6229 * @param n the text node the positinos refer to.
6230 * @param start the start of where to delete from.
6231 * @param end the end of the section to delete, if end == -1 it means the end of the string.
6234 _evas_textblock_node_text_remove_formats_between(Evas_Object_Textblock *o,
6235 Evas_Object_Textblock_Node_Text *n, int start, int end)
6237 Evas_Object_Textblock_Node_Format *itr;
6239 int offset = end - start;
6240 itr = n->format_node;
6243 start -= itr->offset;
6244 if (offset < 0) offset = 0;
6245 if (end < 0) use_end = 0;
6246 while (itr && (itr->text_node == n))
6248 Evas_Object_Textblock_Node_Format *nnode;
6251 /* start is negative when this gets relevant */
6252 if ((offset + start < 0) && use_end)
6256 nnode = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6259 tmp_offset = nnode->offset;
6263 /* Don't do visible adjustments because we are removing the visual
6264 * chars anyway and taking those into account */
6265 _evas_textblock_node_format_remove(o, itr, 0);
6267 start -= tmp_offset;
6274 * Returns the first format in the range between start and end in the textblock
6277 * @param o the textblock object.
6278 * @param n the text node the positinos refer to.
6279 * @param start the start of where to delete from.
6280 * @param end the end of the section to delete, if end == -1 it means the end of the string.
6282 static Evas_Object_Textblock_Node_Format *
6283 _evas_textblock_node_text_get_first_format_between(
6284 Evas_Object_Textblock_Node_Text *n, int start, int end)
6286 Evas_Object_Textblock_Node_Format *itr;
6288 itr = n->format_node;
6289 if (end < 0) use_end = 0;
6290 while (itr && (itr->text_node == n))
6292 start -= itr->offset;
6294 if ((end <= 0) && use_end)
6302 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6308 * Removes a text node and the corresponding format nodes.
6310 * @param o the textblock objec.t
6311 * @param n the node to remove.
6314 _evas_textblock_node_text_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *n)
6316 _evas_textblock_node_text_adjust_offsets_to_start(o, n, 0, -1);
6318 o->text_nodes = _NODE_TEXT(eina_inlist_remove(
6319 EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n)));
6320 _evas_textblock_node_text_free(n);
6325 * Return the position where the formats starts at.
6327 * @param fmt the format to return the position of.
6328 * @return the position of the format in the text node it points to.
6331 _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt)
6333 Evas_Object_Textblock_Node_Text *text;
6334 Evas_Object_Textblock_Node_Format *base_format;
6335 Evas_Object_Textblock_Node_Format *itr;
6336 size_t position = 0;
6339 /* Find the main format node */
6340 text = fmt->text_node;
6341 base_format = text->format_node;
6342 EINA_INLIST_FOREACH(base_format, itr)
6348 position += itr->offset;
6350 return position + fmt->offset;
6354 evas_textblock_cursor_pos_get(const Evas_Textblock_Cursor *cur)
6356 Evas_Object_Textblock *o;
6357 Evas_Object_Textblock_Node_Text *n;
6360 if (!cur) return -1;
6361 if (!cur->node) return 0;
6362 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6364 while (n != cur->node)
6366 npos += eina_ustrbuf_length_get(n->unicode);
6367 n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
6369 return npos + cur->pos;
6373 evas_textblock_cursor_pos_set(Evas_Textblock_Cursor *cur, int _pos)
6375 Evas_Object_Textblock *o;
6376 Evas_Object_Textblock_Node_Text *n;
6380 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6388 pos = (size_t) _pos;
6392 while (n && (pos >= eina_ustrbuf_length_get(n->unicode)))
6394 pos -= eina_ustrbuf_length_get(n->unicode);
6395 n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
6403 else if (o->text_nodes)
6405 /* In case we went pass the last node, we need to put the cursor
6406 * at the absolute end. */
6407 Evas_Object_Textblock_Node_Text *last_n;
6409 last_n = _NODE_TEXT(EINA_INLIST_GET(o->text_nodes)->last);
6410 pos = eina_ustrbuf_length_get(last_n->unicode);
6419 evas_textblock_cursor_line_set(Evas_Textblock_Cursor *cur, int line)
6421 Evas_Object_Textblock *o;
6422 Evas_Object_Textblock_Line *ln;
6423 Evas_Object_Textblock_Item *it;
6425 if (!cur) return EINA_FALSE;
6426 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6427 if (!o->formatted.valid) _relayout(cur->obj);
6429 ln = _find_layout_line_num(cur->obj, line);
6430 if (!ln) return EINA_FALSE;
6431 it = (Evas_Object_Textblock_Item *)ln->items;
6434 cur->pos = it->text_pos;
6435 cur->node = it->text_node;
6441 cur->node = o->text_nodes;
6447 evas_textblock_cursor_compare(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
6449 Eina_Inlist *l1, *l2;
6451 if (!cur1) return 0;
6452 if (!cur2) return 0;
6453 if (cur1->obj != cur2->obj) return 0;
6454 if ((!cur1->node) || (!cur2->node)) return 0;
6455 if (cur1->node == cur2->node)
6457 if (cur1->pos < cur2->pos) return -1; /* cur1 < cur2 */
6458 else if (cur1->pos > cur2->pos) return 1; /* cur2 < cur1 */
6461 for (l1 = EINA_INLIST_GET(cur1->node),
6462 l2 = EINA_INLIST_GET(cur1->node); (l1) || (l2);)
6464 if (l1 == EINA_INLIST_GET(cur2->node)) return 1; /* cur2 < cur 1 */
6465 else if (l2 == EINA_INLIST_GET(cur2->node)) return -1; /* cur1 < cur 2 */
6466 else if (!l1) return -1; /* cur1 < cur 2 */
6467 else if (!l2) return 1; /* cur2 < cur 1 */
6475 evas_textblock_cursor_copy(const Evas_Textblock_Cursor *cur, Evas_Textblock_Cursor *cur_dest)
6478 if (!cur_dest) return;
6479 if (cur->obj != cur_dest->obj) return;
6480 cur_dest->pos = cur->pos;
6481 cur_dest->node = cur->node;
6489 * Free a text node. Shouldn't be used usually, it's better to use
6490 * @ref _evas_textblock_node_text_remove for most cases .
6492 * @param n the text node to free
6493 * @see _evas_textblock_node_text_remove
6496 _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n)
6499 eina_ustrbuf_free(n->unicode);
6503 n->par->text_node = NULL;
6509 * Create a new text node
6511 * @return the new text node.
6513 static Evas_Object_Textblock_Node_Text *
6514 _evas_textblock_node_text_new(void)
6516 Evas_Object_Textblock_Node_Text *n;
6518 n = calloc(1, sizeof(Evas_Object_Textblock_Node_Text));
6519 n->unicode = eina_ustrbuf_new();
6520 /* We want to layout each paragraph at least once. */
6521 n->dirty = EINA_TRUE;
6529 * Break a paragraph. This does not add a PS but only splits the paragraph
6530 * where a ps was just added!
6532 * @param cur the cursor to break at.
6533 * @param fnode the format node of the PS just added.
6534 * @return Returns no value.
6537 _evas_textblock_cursor_break_paragraph(Evas_Textblock_Cursor *cur,
6538 Evas_Object_Textblock_Node_Format *fnode)
6540 Evas_Object_Textblock *o;
6541 Evas_Object_Textblock_Node_Text *n;
6544 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6546 n = _evas_textblock_node_text_new();
6547 o->text_nodes = _NODE_TEXT(eina_inlist_append_relative(
6548 EINA_INLIST_GET(o->text_nodes),
6550 EINA_INLIST_GET(cur->node)));
6551 /* Handle text and format changes. */
6554 Evas_Object_Textblock_Node_Format *nnode;
6556 const Eina_Unicode *text;
6558 /* If there was a format node in the delete range,
6559 * make it our format and update the text_node fields,
6560 * otherwise, use the paragraph separator
6561 * of the previous paragraph. */
6562 nnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
6563 if (nnode && (nnode->text_node == cur->node))
6565 n->format_node = nnode;
6566 nnode->offset--; /* We don't have to take the replacement char
6567 into account anymore */
6568 while (nnode && (nnode->text_node == cur->node))
6570 nnode->text_node = n;
6571 nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
6576 n->format_node = fnode;
6579 /* cur->pos now points to the PS, move after. */
6580 start = cur->pos + 1;
6581 len = eina_ustrbuf_length_get(cur->node->unicode) - start;
6584 text = eina_ustrbuf_string_get(cur->node->unicode);
6585 eina_ustrbuf_append_length(n->unicode, text + start, len);
6586 eina_ustrbuf_remove(cur->node->unicode, start, start + len);
6587 cur->node->dirty = EINA_TRUE;
6592 fnode = o->format_nodes;
6595 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->last);
6597 n->format_node = fnode;
6603 * Set the node and offset of all the curs after cur.
6605 * @param cur the cursor.
6606 * @param n the current textblock node.
6607 * @param new_node the new node to set.
6610 _evas_textblock_cursors_set_node(Evas_Object_Textblock *o,
6611 const Evas_Object_Textblock_Node_Text *n,
6612 Evas_Object_Textblock_Node_Text *new_node)
6615 Evas_Textblock_Cursor *data;
6617 if (n == o->cursor->node)
6620 o->cursor->node = new_node;
6622 EINA_LIST_FOREACH(o->cursors, l, data)
6624 if (n == data->node)
6627 data->node = new_node;
6634 * Update the offset of all the cursors after cur.
6636 * @param cur the cursor.
6637 * @param n the current textblock node.
6638 * @param start the starting pos.
6639 * @param offset how much to adjust (can be negative).
6642 _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor *cur,
6643 const Evas_Object_Textblock_Node_Text *n,
6644 size_t start, int offset)
6647 Evas_Textblock_Cursor *data;
6648 Evas_Object_Textblock *o;
6649 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6651 if (cur != o->cursor)
6653 if ((n == o->cursor->node) &&
6654 (o->cursor->pos > start))
6656 if ((offset < 0) && (o->cursor->pos <= (size_t) (-1 * offset)))
6662 o->cursor->pos += offset;
6666 EINA_LIST_FOREACH(o->cursors, l, data)
6670 if ((n == data->node) &&
6671 (data->pos > start))
6673 if ((offset < 0) && (data->pos <= (size_t) (-1 * offset)))
6679 data->pos += offset;
6682 else if (!data->node)
6684 data->node = o->text_nodes;
6693 * Mark that the textblock has changed.
6695 * @param o the textblock object.
6696 * @param obj the evas object.
6699 _evas_textblock_changed(Evas_Object_Textblock *o, Evas_Object *obj)
6701 o->formatted.valid = 0;
6702 o->native.valid = 0;
6703 o->content_changed = 1;
6706 free(o->markup_text);
6707 o->markup_text = NULL;
6710 evas_object_change(obj);
6714 _evas_textblock_invalidate_all(Evas_Object_Textblock *o)
6716 Evas_Object_Textblock_Node_Text *n;
6718 EINA_INLIST_FOREACH(o->text_nodes, n)
6720 n->dirty = EINA_TRUE;
6725 evas_textblock_cursor_text_append(Evas_Textblock_Cursor *cur, const char *_text)
6727 Evas_Object_Textblock *o;
6728 Evas_Object_Textblock_Node_Text *n;
6729 Evas_Object_Textblock_Node_Format *fnode = NULL;
6734 text = eina_unicode_utf8_to_unicode(_text, &len);
6735 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6740 Evas_Object_Textblock_Node_Format *nnode;
6741 fnode = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
6742 fnode = _evas_textblock_node_format_last_at_off(fnode);
6743 /* find the node after the current in the same paragraph
6744 * either we find one and then take the next, or we try to get
6745 * the first for the paragraph which must be after our position */
6748 if (!evas_textblock_cursor_format_is_visible_get(cur))
6750 nnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
6751 if (nnode && (nnode->text_node == n))
6763 fnode = n->format_node;
6766 else if (o->text_nodes)
6768 cur->node = o->text_nodes;
6773 n = _evas_textblock_node_text_new();
6774 o->text_nodes = _NODE_TEXT(eina_inlist_append(
6775 EINA_INLIST_GET(o->text_nodes),
6776 EINA_INLIST_GET(n)));
6780 eina_ustrbuf_insert_length(n->unicode, text, len, cur->pos);
6781 /* Advance the formats */
6782 if (fnode && (fnode->text_node == cur->node))
6783 fnode->offset += len;
6785 /* Update all the cursors after our position. */
6786 _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, len);
6788 _evas_textblock_changed(o, cur->obj);
6789 n->dirty = EINA_TRUE;
6792 if (!o->cursor->node)
6793 o->cursor->node = o->text_nodes;
6798 evas_textblock_cursor_text_prepend(Evas_Textblock_Cursor *cur, const char *_text)
6801 /*append is essentially prepend without advancing */
6802 len = evas_textblock_cursor_text_append(cur, _text);
6803 cur->pos += len; /*Advance */
6809 * Free a format node
6811 * @param o the textblock object
6812 * @param n the format node to free
6815 _evas_textblock_node_format_free(Evas_Object_Textblock *o,
6816 Evas_Object_Textblock_Node_Format *n)
6819 eina_stringshare_del(n->format);
6820 eina_stringshare_del(n->orig_format);
6821 if (n->anchor == ANCHOR_ITEM)
6822 o->anchors_item = eina_list_remove(o->anchors_item, n);
6823 else if (n->anchor == ANCHOR_A)
6824 o->anchors_a = eina_list_remove(o->anchors_a, n);
6830 * Create a new format node.
6832 * @param format the text to create the format node from.
6833 * @param o the textblock object.
6834 * @return Returns the new format node
6836 static Evas_Object_Textblock_Node_Format *
6837 _evas_textblock_node_format_new(Evas_Object_Textblock *o, const char *_format)
6839 Evas_Object_Textblock_Node_Format *n;
6840 const char *format = _format;
6842 n = calloc(1, sizeof(Evas_Object_Textblock_Node_Format));
6843 /* Create orig_format and format */
6844 if (format[0] == '<')
6850 format++; /* Advance after '<' */
6851 format_len = strlen(format);
6852 if (format[format_len - 1] == '>')
6853 format_len--; /* We don't care about '>' */
6855 match = _style_match_tag(o->style, format, format_len, &replace_len);
6858 if ((match[0] == '+') || (match[0] == '-'))
6861 norm_format = malloc(format_len + 2 + 1);
6862 memcpy(norm_format, match, 2);
6863 memcpy(norm_format + 2, format, format_len);
6864 norm_format[format_len + 2] = '\0';
6866 eina_stringshare_add_length(norm_format, format_len + 2);
6872 eina_stringshare_add_length(format, format_len);
6874 n->format = eina_stringshare_add(match);
6880 norm_format = malloc(format_len + 2 + 1);
6883 if (format[0] == '/')
6885 memcpy(norm_format, "- ", 2);
6886 memcpy(norm_format + 2, format + 1, format_len - 1);
6887 norm_format[format_len + 2 - 1] = '\0';
6891 memcpy(norm_format, "+ ", 2);
6892 memcpy(norm_format + 2, format, format_len);
6893 norm_format[format_len + 2] = '\0';
6895 n->orig_format = eina_stringshare_add(norm_format);
6898 n->format = eina_stringshare_ref(n->orig_format);
6901 /* Just use as is, it's a special format. */
6904 n->orig_format = eina_stringshare_add(format);
6905 n->format = eina_stringshare_ref(n->orig_format);
6910 _evas_textblock_format_is_visible(n, format);
6911 if (n->anchor == ANCHOR_A)
6913 o->anchors_a = eina_list_append(o->anchors_a, n);
6915 else if (n->anchor == ANCHOR_ITEM)
6917 o->anchors_item = eina_list_append(o->anchors_item, n);
6925 _evas_textblock_cursor_is_at_the_end(const Evas_Textblock_Cursor *cur)
6927 const Eina_Unicode *text;
6929 if (!cur) return EINA_FALSE;
6930 if (!cur->node) return EINA_FALSE;
6931 text = eina_ustrbuf_string_get(cur->node->unicode);
6932 return ((text[cur->pos] == 0) && (!EINA_INLIST_GET(cur->node)->next)) ?
6933 EINA_TRUE : EINA_FALSE;
6937 evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, const char *format)
6939 Evas_Object_Textblock *o;
6940 Evas_Object_Textblock_Node_Format *n;
6941 Eina_Bool is_visible;
6943 if (!cur) return EINA_FALSE;
6944 if ((!format) || (format[0] == 0)) return EINA_FALSE;
6945 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6946 /* We should always have at least one text node */
6949 evas_textblock_cursor_text_prepend(cur, "");
6952 n = _evas_textblock_node_format_new(o, format);
6953 is_visible = n->visible;
6957 o->format_nodes = _NODE_FORMAT(eina_inlist_append(
6958 EINA_INLIST_GET(o->format_nodes),
6959 EINA_INLIST_GET(n)));
6961 n->text_node = (EINA_INLIST_GET(n)->prev) ?
6962 _NODE_FORMAT(EINA_INLIST_GET(n)->prev)->text_node :
6964 cur->node = n->text_node;
6968 Evas_Object_Textblock_Node_Format *fmt;
6969 fmt = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
6970 n->text_node = cur->node;
6973 o->format_nodes = _NODE_FORMAT(eina_inlist_prepend(
6974 EINA_INLIST_GET(o->format_nodes),
6975 EINA_INLIST_GET(n)));
6976 n->offset = cur->pos;
6980 if (evas_textblock_cursor_format_is_visible_get(cur))
6982 o->format_nodes = _NODE_FORMAT(eina_inlist_prepend_relative(
6983 EINA_INLIST_GET(o->format_nodes),
6985 EINA_INLIST_GET(fmt)
6987 n->offset = fmt->offset;
6988 if (fmt->text_node->format_node == fmt)
6990 fmt->text_node->format_node = n;
6995 fmt = _evas_textblock_node_format_last_at_off(fmt);
6996 o->format_nodes = _NODE_FORMAT(eina_inlist_append_relative(
6997 EINA_INLIST_GET(o->format_nodes),
6999 EINA_INLIST_GET(fmt)
7001 if (fmt->text_node != cur->node)
7003 n->offset = cur->pos;
7007 n->offset = cur->pos -
7008 _evas_textblock_node_format_pos_get(fmt);
7012 /* Adjust differently if we insert a format char */
7015 _evas_textblock_node_format_adjust_offset(o, cur->node, n,
7020 _evas_textblock_node_format_adjust_offset(o, cur->node, n,
7024 if (!fmt || (fmt->text_node != cur->node))
7026 cur->node->format_node = n;
7029 if (is_visible && cur->node)
7031 Eina_Unicode insert_char;
7032 /* Insert a visual representation according to the type of the
7034 if (_IS_PARAGRAPH_SEPARATOR(o, format))
7035 insert_char = _PARAGRAPH_SEPARATOR;
7036 else if (_IS_LINE_SEPARATOR(format))
7038 else if (_IS_TAB(format))
7041 insert_char = EVAS_TEXTBLOCK_REPLACEMENT_CHAR;
7043 eina_ustrbuf_insert_char(cur->node->unicode, insert_char, cur->pos);
7045 /* Advance all the cursors after our cursor */
7046 _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, 1);
7047 if (_IS_PARAGRAPH_SEPARATOR(o, format))
7049 _evas_textblock_cursor_break_paragraph(cur, n);
7053 /* Handle visible format nodes here */
7054 cur->node->dirty = EINA_TRUE;
7055 n->new = EINA_FALSE;
7060 o->format_changed = EINA_TRUE;
7063 _evas_textblock_changed(o, cur->obj);
7065 if (!o->cursor->node)
7066 o->cursor->node = o->text_nodes;
7071 evas_textblock_cursor_format_prepend(Evas_Textblock_Cursor *cur, const char *format)
7073 Eina_Bool is_visible;
7074 /* append is essentially prepend without advancing */
7075 is_visible = evas_textblock_cursor_format_append(cur, format);
7078 /* Advance after the replacement char */
7079 evas_textblock_cursor_char_next(cur);
7087 evas_textblock_cursor_char_delete(Evas_Textblock_Cursor *cur)
7089 Evas_Object_Textblock *o;
7090 Evas_Object_Textblock_Node_Text *n, *n2;
7091 const Eina_Unicode *text;
7094 if (!cur || !cur->node) return;
7095 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7098 text = eina_ustrbuf_string_get(n->unicode);
7105 if (chr == 0) return;
7107 eina_ustrbuf_remove(n->unicode, cur->pos, ind);
7108 /* Remove a format node if needed, and remove the char only if the
7109 * fmt node is not visible */
7111 Eina_Bool should_merge = EINA_FALSE;
7112 Evas_Object_Textblock_Node_Format *fmt, *fmt2;
7113 fmt = _evas_textblock_cursor_node_format_at_pos_get(cur);
7116 const char *format = NULL;
7117 Evas_Object_Textblock_Node_Format *last_fmt;
7118 /* If there's a PS it must be the last become it delimits paragraphs */
7119 last_fmt = _evas_textblock_node_format_last_at_off(fmt);
7120 format = last_fmt->format;
7121 if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
7123 /* If it was a paragraph separator, we should merge the
7124 * current with the next, there must be a next. */
7125 should_merge = EINA_TRUE;
7127 /* If a singnular, mark as invisible, so we'll delete it. */
7128 if (!format || ((*format != '+') && (*format != '-')))
7130 last_fmt->visible = EINA_FALSE;
7134 fmt2 = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
7135 fmt2 = _evas_textblock_node_format_last_at_off(fmt2);
7136 _evas_textblock_node_format_adjust_offset(o, cur->node, fmt2,
7141 _evas_textblock_cursor_nodes_merge(cur);
7144 _evas_textblock_node_format_remove_matching(o, fmt);
7147 if (cur->pos == eina_ustrbuf_length_get(n->unicode))
7149 n2 = _NODE_TEXT(EINA_INLIST_GET(n)->next);
7157 _evas_textblock_cursors_update_offset(cur, n, ppos, -(ind - ppos));
7158 _evas_textblock_changed(o, cur->obj);
7159 cur->node->dirty = EINA_TRUE;
7163 evas_textblock_cursor_range_delete(Evas_Textblock_Cursor *cur1, Evas_Textblock_Cursor *cur2)
7165 Evas_Object_Textblock_Node_Format *fnode = NULL;
7166 Evas_Object_Textblock *o;
7167 Evas_Object_Textblock_Node_Text *n1, *n2;
7168 Eina_Bool should_merge = EINA_FALSE, reset_cursor = EINA_FALSE;
7170 if (!cur1 || !cur1->node) return;
7171 if (!cur2 || !cur2->node) return;
7172 if (cur1->obj != cur2->obj) return;
7173 o = (Evas_Object_Textblock *)(cur1->obj->object_data);
7174 if (evas_textblock_cursor_compare(cur1, cur2) > 0)
7176 Evas_Textblock_Cursor *tc;
7184 if ((evas_textblock_cursor_compare(o->cursor, cur1) >= 0) &&
7185 (evas_textblock_cursor_compare(cur2, o->cursor) >= 0))
7187 reset_cursor = EINA_TRUE;
7193 if ((cur1->pos == 0) &&
7194 (cur2->pos == eina_ustrbuf_length_get(n1->unicode)))
7196 _evas_textblock_node_text_remove_formats_between(o, n1, 0, -1);
7200 should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o,
7201 n1, cur1->pos, cur2->pos);
7203 eina_ustrbuf_remove(n1->unicode, cur1->pos, cur2->pos);
7204 _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos, - (cur2->pos - cur1->pos));
7208 Evas_Object_Textblock_Node_Text *n;
7210 _evas_textblock_node_text_adjust_offsets_to_start(o, n1, cur1->pos, -1);
7211 n = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7212 /* Remove all the text nodes between */
7213 while (n && (n != n2))
7215 Evas_Object_Textblock_Node_Text *nnode;
7217 nnode = _NODE_TEXT(EINA_INLIST_GET(n)->next);
7218 _evas_textblock_cursors_set_node(o, n, n1);
7219 _evas_textblock_node_text_remove(o, n);
7222 should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o, n2,
7225 /* Remove the formats and the strings in the first and last nodes */
7226 len = eina_ustrbuf_length_get(n1->unicode);
7227 eina_ustrbuf_remove(n1->unicode, cur1->pos, len);
7228 eina_ustrbuf_remove(n2->unicode, 0, cur2->pos);
7229 /* Merge the nodes because we removed the PS */
7230 _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos,
7232 _evas_textblock_cursors_update_offset(cur2, cur2->node, 0, - cur2->pos);
7233 _evas_textblock_nodes_merge(o, n1);
7235 fnode = _evas_textblock_cursor_node_format_at_pos_get(cur1);
7239 /* We call this function instead of the cursor one because we already
7240 * updated the cursors */
7241 _evas_textblock_nodes_merge(o, n1);
7243 _evas_textblock_node_format_remove_matching(o, fnode);
7245 evas_textblock_cursor_copy(cur1, cur2);
7247 evas_textblock_cursor_copy(cur1, o->cursor);
7249 _evas_textblock_changed(o, cur1->obj);
7250 n1->dirty = n2->dirty = EINA_TRUE;
7255 evas_textblock_cursor_content_get(const Evas_Textblock_Cursor *cur)
7257 const Eina_Unicode *ustr;
7258 Eina_Unicode buf[2];
7260 if (!cur || !cur->node) return NULL;
7261 if (evas_textblock_cursor_format_is_visible_get(cur))
7267 fstr = evas_textblock_node_format_text_get(
7268 _evas_textblock_node_visible_at_pos_get(
7269 evas_textblock_cursor_format_get(cur)));
7274 if (*fstr == '-') pop = 1;
7275 while ((*fstr == ' ') || (*fstr == '+') || (*fstr == '-')) fstr++;
7282 ret = tmp = malloc(len + 3 + 1); /* </> and the null */
7283 memcpy(tmp, "</", 2);
7288 ret = tmp = malloc(len + 2 + 1); /* <> and the null */
7292 memcpy(tmp, fstr, len);
7293 memcpy(tmp + len, ">", 2); /* Including the null */
7299 ustr = eina_ustrbuf_string_get(cur->node->unicode);
7300 buf[0] = ustr[cur->pos];
7302 s = eina_unicode_unicode_to_utf8(buf, NULL);
7308 _evas_textblock_cursor_range_text_markup_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2)
7310 Evas_Object_Textblock *o;
7311 Evas_Object_Textblock_Node_Text *tnode;
7313 Evas_Textblock_Cursor *cur2;
7314 buf = eina_strbuf_new();
7316 if (!cur1 || !cur1->node) return NULL;
7317 if (!_cur2 || !_cur2->node) return NULL;
7318 if (cur1->obj != _cur2->obj) return NULL;
7319 o = (Evas_Object_Textblock *)(cur1->obj->object_data);
7320 if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
7322 const Evas_Textblock_Cursor *tc;
7328 /* Work on a local copy of the cur */
7329 cur2 = alloca(sizeof(Evas_Textblock_Cursor));
7330 cur2->obj = _cur2->obj;
7331 evas_textblock_cursor_copy(_cur2, cur2);
7333 /* Parse the text between the cursors. */
7334 for (tnode = cur1->node ; tnode ;
7335 tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next))
7337 Evas_Object_Textblock_Node_Format *fnode;
7338 Eina_Unicode *text_base, *text;
7342 eina_unicode_strndup(eina_ustrbuf_string_get(tnode->unicode),
7343 eina_ustrbuf_length_get(tnode->unicode));
7344 if (tnode == cur2->node)
7346 fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7347 cur1->pos, cur2->pos);
7349 else if (tnode == cur1->node)
7351 fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7356 fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7359 /* Init the offset so the first one will count starting from cur1->pos
7360 * and not the previous format node */
7361 if (tnode == cur1->node)
7365 off = _evas_textblock_node_format_pos_get(fnode) -
7366 cur1->pos - fnode->offset;
7374 while (fnode && (fnode->text_node == tnode))
7376 Eina_Unicode tmp_ch;
7377 off += fnode->offset;
7378 if ((tnode == cur2->node) &&
7379 ((size_t) (text - text_base + off) >= cur2->pos))
7383 /* No need to skip on the first run */
7385 text[off] = 0; /* Null terminate the part of the string */
7386 _markup_get_text_append(buf, text);
7387 _markup_get_format_append(o, buf, fnode);
7388 text[off] = tmp_ch; /* Restore the char */
7399 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7401 /* If we got to the last node, stop and add the rest outside */
7402 if (cur2->node == tnode)
7404 /* Add the rest, skip replacement */
7405 /* Don't go past the second cursor pos */
7406 text_base[cur2->pos] = '\0';
7407 _markup_get_text_append(buf, text);
7413 /* Add the rest, skip replacement */
7414 _markup_get_text_append(buf, text);
7418 /* return the string */
7421 ret = eina_strbuf_string_steal(buf);
7422 eina_strbuf_free(buf);
7428 _evas_textblock_cursor_range_text_plain_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2)
7431 Evas_Object_Textblock_Node_Text *n1, *n2;
7432 Evas_Textblock_Cursor *cur2;
7434 buf = eina_ustrbuf_new();
7436 if (!cur1 || !cur1->node) return NULL;
7437 if (!_cur2 || !_cur2->node) return NULL;
7438 if (cur1->obj != _cur2->obj) return NULL;
7439 if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
7441 const Evas_Textblock_Cursor *tc;
7449 /* Work on a local copy of the cur */
7450 cur2 = alloca(sizeof(Evas_Textblock_Cursor));
7451 cur2->obj = _cur2->obj;
7452 evas_textblock_cursor_copy(_cur2, cur2);
7457 const Eina_Unicode *tmp;
7458 tmp = eina_ustrbuf_string_get(n1->unicode);
7459 eina_ustrbuf_append_length(buf, tmp + cur1->pos, cur2->pos - cur1->pos);
7463 const Eina_Unicode *tmp;
7464 tmp = eina_ustrbuf_string_get(n1->unicode);
7465 eina_ustrbuf_append(buf, tmp + cur1->pos);
7466 n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7469 tmp = eina_ustrbuf_string_get(n1->unicode);
7470 eina_ustrbuf_append_length(buf, tmp,
7471 eina_ustrbuf_length_get(n1->unicode));
7472 n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7474 tmp = eina_ustrbuf_string_get(n2->unicode);
7475 eina_ustrbuf_append_length(buf, tmp, cur2->pos);
7478 /* Free and return */
7481 ret = eina_unicode_unicode_to_utf8(eina_ustrbuf_string_get(buf), NULL);
7482 eina_ustrbuf_free(buf);
7488 evas_textblock_cursor_range_formats_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
7490 Evas_Object *obj = cur1->obj;
7491 Eina_List *ret = NULL;
7492 Evas_Object_Textblock_Node_Text *n1, *n2;
7493 Evas_Object_Textblock_Node_Format *first, *last;
7494 TB_HEAD_RETURN(NULL);
7495 if (!cur1 || !cur1->node) return NULL;
7496 if (!cur2 || !cur2->node) return NULL;
7497 if (cur1->obj != cur2->obj) return NULL;
7498 if (evas_textblock_cursor_compare(cur1, cur2) > 0)
7500 const Evas_Textblock_Cursor *tc;
7509 /* FIXME: Change first and last getting to format_before_or_at_pos_get */
7511 last = n2->format_node;
7513 /* If n2->format_node is NULL, we don't have formats in the tb/range. */
7516 /* If the found format is on our text node, we should go to the last
7517 * one, otherwise, the one we found is good enough. */
7518 if (last->text_node == n2)
7520 Evas_Object_Textblock_Node_Format *fnode = last;
7521 while (fnode && (fnode->text_node == n2))
7524 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7528 /* If the first format node is within the range (i.e points to n1) or if
7529 * we have other formats in the range, go through them */
7530 first = n1->format_node;
7531 if ((first->text_node == n1) || (first != last))
7533 Evas_Object_Textblock_Node_Format *fnode = first;
7534 /* Go to the first one in the range */
7535 if (first->text_node != n1)
7537 first = _NODE_FORMAT(EINA_INLIST_GET(first)->next);
7542 ret = eina_list_append(ret, fnode);
7545 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7554 evas_textblock_cursor_range_text_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2, Evas_Textblock_Text_Type format)
7556 if (format == EVAS_TEXTBLOCK_TEXT_MARKUP)
7557 return _evas_textblock_cursor_range_text_markup_get(cur1, cur2);
7558 else if (format == EVAS_TEXTBLOCK_TEXT_PLAIN)
7559 return _evas_textblock_cursor_range_text_plain_get(cur1, cur2);
7561 return NULL; /* Not yet supported */
7565 evas_textblock_cursor_paragraph_text_get(const Evas_Textblock_Cursor *cur)
7567 Evas_Textblock_Cursor cur1, cur2;
7568 if (!cur) return NULL;
7569 if (!cur->node) return NULL;
7570 if (cur->node->utf8)
7572 free(cur->node->utf8);
7574 cur1.obj = cur2.obj = cur->obj;
7575 cur1.node = cur2.node = cur->node;
7576 evas_textblock_cursor_paragraph_char_first(&cur1);
7577 evas_textblock_cursor_paragraph_char_last(&cur2);
7579 cur->node->utf8 = evas_textblock_cursor_range_text_get(&cur1, &cur2,
7580 EVAS_TEXTBLOCK_TEXT_MARKUP);
7581 return cur->node->utf8;
7585 evas_textblock_cursor_paragraph_text_length_get(const Evas_Textblock_Cursor *cur)
7588 if (!cur) return -1;
7589 if (!cur->node) return -1;
7590 len = eina_ustrbuf_length_get(cur->node->unicode);
7592 if (EINA_INLIST_GET(cur->node)->next)
7593 return len - 1; /* Remove the paragraph separator */
7598 EAPI const Evas_Object_Textblock_Node_Format *
7599 evas_textblock_cursor_format_get(const Evas_Textblock_Cursor *cur)
7601 if (!cur) return NULL;
7602 if (!cur->node) return NULL;
7603 return _evas_textblock_cursor_node_format_at_pos_get(cur);
7607 evas_textblock_node_format_text_get(const Evas_Object_Textblock_Node_Format *fmt)
7609 if (!fmt) return NULL;
7610 return fmt->orig_format;
7614 evas_textblock_cursor_at_format_set(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *fmt)
7616 if (!fmt || !cur) return;
7617 cur->node = fmt->text_node;
7618 cur->pos = _evas_textblock_node_format_pos_get(fmt);
7622 evas_textblock_cursor_format_is_visible_get(const Evas_Textblock_Cursor *cur)
7624 const Eina_Unicode *text;
7626 if (!cur) return EINA_FALSE;
7627 if (!cur->node) return EINA_FALSE;
7628 text = eina_ustrbuf_string_get(cur->node->unicode);
7629 return EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(text[cur->pos]);
7633 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)
7636 const Evas_Textblock_Cursor *dir_cur;
7637 Evas_Textblock_Cursor cur2;
7638 Evas_Object_Textblock *o;
7639 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7640 if (!o->formatted.valid) _relayout(cur->obj);
7643 if (ctype == EVAS_TEXTBLOCK_CURSOR_UNDER)
7645 ret = evas_textblock_cursor_pen_geometry_get(cur, cx, cy, cw, ch);
7647 else if (ctype == EVAS_TEXTBLOCK_CURSOR_BEFORE)
7649 /* In the case of a "before cursor", we should get the coordinates
7650 * of just after the previous char (which in bidi text may not be
7651 * just before the current char). */
7652 Evas_Coord x, y, h, w;
7653 Evas_Object_Textblock_Node_Format *fmt;
7655 /* If it's at the end of the line, we want to get the position, not
7656 * the position of the previous */
7657 if ((cur->pos > 0) && !_evas_textblock_cursor_is_at_the_end(cur))
7659 Eina_Bool before_char = EINA_FALSE;
7660 cur2.obj = cur->obj;
7661 evas_textblock_cursor_copy(cur, &cur2);
7662 evas_textblock_cursor_char_prev(&cur2);
7664 fmt = _evas_textblock_cursor_node_format_at_pos_get(&cur2);
7666 if (!fmt || !_IS_LINE_SEPARATOR(fmt->format))
7669 before_char = EINA_FALSE;
7673 before_char = EINA_TRUE;
7675 ret = evas_textblock_cursor_pen_geometry_get(
7676 dir_cur, &x, &y, &w, &h);
7678 /* Adjust if the char is an rtl char */
7681 Eina_Bool is_rtl = EINA_FALSE;
7682 if (dir_cur->node->par->is_bidi)
7684 Evas_Object_Textblock_Line *ln;
7685 Evas_Object_Textblock_Item *it;
7686 _find_layout_item_match(dir_cur, &ln, &it);
7687 if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
7688 (_ITEM_TEXT(it)->text_props.bidi.dir ==
7689 EVAS_BIDI_DIRECTION_RTL))
7691 else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
7692 (_ITEM_FORMAT(it)->bidi_dir ==
7693 EVAS_BIDI_DIRECTION_RTL))
7697 if ((!before_char && is_rtl) ||
7698 (before_char && !is_rtl))
7700 /* Just don't advance the width */
7706 else if (cur->pos == 0)
7708 ret = evas_textblock_cursor_pen_geometry_get(
7709 dir_cur, &x, &y, &w, &h);
7711 Eina_Bool is_rtl = EINA_FALSE;
7712 if (dir_cur->node->par->is_bidi)
7714 Evas_Object_Textblock_Line *ln;
7715 Evas_Object_Textblock_Item *it;
7716 _find_layout_item_match(dir_cur, &ln, &it);
7717 if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
7718 (_ITEM_TEXT(it)->text_props.bidi.dir ==
7719 EVAS_BIDI_DIRECTION_RTL))
7721 else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
7722 (_ITEM_FORMAT(it)->bidi_dir ==
7723 EVAS_BIDI_DIRECTION_RTL))
7727 /* Adjust if the char is an rtl char */
7728 if ((ret >= 0) && (!is_rtl))
7730 /* Just don't advance the width */
7737 ret = evas_textblock_cursor_pen_geometry_get(
7738 dir_cur, &x, &y, &w, &h);
7742 if (cx) *cx = x + w;
7749 if (dir && dir_cur && dir_cur->node)
7752 Eina_Bool is_rtl = EINA_FALSE;
7753 if (dir_cur->node->par->is_bidi)
7755 Evas_Object_Textblock_Line *ln;
7756 Evas_Object_Textblock_Item *it;
7757 _find_layout_item_match(dir_cur, &ln, &it);
7758 if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
7759 (_ITEM_TEXT(it)->text_props.bidi.dir ==
7760 EVAS_BIDI_DIRECTION_RTL))
7762 else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
7763 (_ITEM_FORMAT(it)->bidi_dir ==
7764 EVAS_BIDI_DIRECTION_RTL))
7768 if (_evas_textblock_cursor_is_at_the_end(dir_cur) && (dir_cur->pos > 0))
7771 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
7773 else if (dir_cur->pos > 0)
7776 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
7781 *dir = EVAS_BIDI_DIRECTION_LTR;
7789 * Returns the geometry/pen position (depending on query_func) of the char
7792 * @param cur the position of the char.
7793 * @param query_func the query function to use.
7794 * @param cx the x of the char (or pen_x in the case of pen position).
7795 * @param cy the y of the char.
7796 * @param cw the w of the char (or advance in the case pen position).
7797 * @param ch the h of the char.
7798 * @return line number of the char on success, -1 on error.
7801 _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)
7803 Evas_Object_Textblock *o;
7804 Evas_Object_Textblock_Line *ln = NULL;
7805 Evas_Object_Textblock_Item *it = NULL;
7806 Evas_Object_Textblock_Text_Item *ti = NULL;
7807 Evas_Object_Textblock_Format_Item *fi = NULL;
7808 int x = 0, y = 0, w = 0, h = 0;
7810 Eina_Bool previous_format;
7812 if (!cur) return -1;
7813 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7814 if (!o->formatted.valid) _relayout(cur->obj);
7820 if (!o->paragraphs) return -1;
7821 ln = o->paragraphs->lines;
7823 if (cx) *cx = ln->x;
7824 if (cy) *cy = ln->par->y + ln->y;
7825 if (cw) *cw = ln->w;
7826 if (ch) *ch = ln->h;
7827 return ln->par->line_no + ln->line_no;
7833 previous_format = _find_layout_item_match(cur, &ln, &it);
7838 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
7840 ti = _ITEM_TEXT(it);
7844 fi = _ITEM_FORMAT(it);
7849 pos = cur->pos - ti->parent.text_pos;
7851 if (pos < 0) pos = 0;
7852 if (ti->parent.format->font.font)
7854 query_func(cur->ENDT,
7855 ti->parent.format->font.font,
7861 x += ln->x + _ITEM(ti)->x;
7867 y = ln->par->y + ln->y;
7872 if (previous_format)
7874 if (_IS_LINE_SEPARATOR(fi->item))
7877 y = ln->par->y + ln->y + ln->h;
7882 if (ln->par->direction == EVAS_BIDI_DIRECTION_RTL)
7891 y = ln->par->y + ln->y;
7898 x = ln->x + _ITEM(fi)->x;
7899 y = ln->par->y + ln->y;
7912 return ln->par->line_no + ln->line_no;
7916 evas_textblock_cursor_char_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
7918 return _evas_textblock_cursor_char_pen_geometry_common_get(
7919 cur->ENFN->font_char_coords_get, cur, cx, cy, cw, ch);
7923 evas_textblock_cursor_pen_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
7925 return _evas_textblock_cursor_char_pen_geometry_common_get(
7926 cur->ENFN->font_pen_coords_get, cur, cx, cy, cw, ch);
7930 evas_textblock_cursor_line_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
7932 Evas_Object_Textblock *o;
7933 Evas_Object_Textblock_Line *ln = NULL;
7934 Evas_Object_Textblock_Item *it = NULL;
7937 if (!cur) return -1;
7938 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7939 if (!o->formatted.valid) _relayout(cur->obj);
7942 ln = o->paragraphs->lines;
7946 _find_layout_item_match(cur, &ln, &it);
7950 y = ln->par->y + ln->y;
7957 return ln->par->line_no + ln->line_no;
7961 evas_textblock_cursor_visible_range_get(Evas_Textblock_Cursor *start, Evas_Textblock_Cursor *end)
7964 Evas_Object *obj = start->obj;
7965 TB_HEAD_RETURN(EINA_FALSE);
7966 /* Clip is relative to the object */
7967 cy = obj->cur.cache.clip.y - obj->cur.geometry.y;
7968 ch = obj->cur.cache.clip.h;
7969 evas_textblock_cursor_line_coord_set(start, cy);
7970 evas_textblock_cursor_line_coord_set(end, cy + ch);
7971 evas_textblock_cursor_line_char_last(end);
7977 evas_textblock_cursor_char_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord x, Evas_Coord y)
7979 Evas_Object_Textblock *o;
7980 Evas_Object_Textblock_Paragraph *found_par;
7981 Evas_Object_Textblock_Line *ln;
7982 Evas_Object_Textblock_Item *it = NULL;
7984 if (!cur) return EINA_FALSE;
7985 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7986 if (!o->formatted.valid) _relayout(cur->obj);
7987 x += o->style_pad.l;
7988 y += o->style_pad.t;
7990 found_par = _layout_find_paragraph_by_y(o, y);
7993 _layout_paragraph_render(o, found_par);
7994 EINA_INLIST_FOREACH(found_par->lines, ln)
7996 if (ln->par->y + ln->y > y) break;
7997 if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
7999 /* If before or after the line, go to start/end according
8000 * to paragraph direction. */
8003 cur->pos = ln->items->text_pos;
8004 cur->node = found_par->text_node;
8005 if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
8007 evas_textblock_cursor_line_char_last(cur);
8011 evas_textblock_cursor_line_char_first(cur);
8015 else if (x >= ln->x + ln->w)
8017 cur->pos = ln->items->text_pos;
8018 cur->node = found_par->text_node;
8019 if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
8021 evas_textblock_cursor_line_char_first(cur);
8025 evas_textblock_cursor_line_char_last(cur);
8030 EINA_INLIST_FOREACH(ln->items, it)
8032 if (((it->x + ln->x) <= x) && (((it->x + ln->x) + it->adv) > x))
8034 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8038 Evas_Object_Textblock_Text_Item *ti;
8039 ti = _ITEM_TEXT(it);
8042 if (ti->parent.format->font.font)
8043 pos = cur->ENFN->font_char_at_coords_get(
8045 ti->parent.format->font.font,
8047 x - it->x - ln->x, 0,
8048 &cx, &cy, &cw, &ch);
8051 cur->pos = pos + it->text_pos;
8052 cur->node = it->text_node;
8057 Evas_Object_Textblock_Format_Item *fi;
8058 fi = _ITEM_FORMAT(it);
8059 cur->pos = fi->parent.text_pos;
8060 cur->node = found_par->text_node;
8068 else if (o->paragraphs && (y >= o->paragraphs->y + o->formatted.h))
8070 /* If we are after the last paragraph, use the last position in the
8072 evas_textblock_cursor_paragraph_last(cur);
8075 else if (o->paragraphs && (y < o->paragraphs->y))
8077 evas_textblock_cursor_paragraph_first(cur);
8085 evas_textblock_cursor_line_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord y)
8087 Evas_Object_Textblock *o;
8088 Evas_Object_Textblock_Paragraph *found_par;
8089 Evas_Object_Textblock_Line *ln;
8091 if (!cur) return -1;
8092 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8093 if (!o->formatted.valid) _relayout(cur->obj);
8094 y += o->style_pad.t;
8096 found_par = _layout_find_paragraph_by_y(o, y);
8100 _layout_paragraph_render(o, found_par);
8101 EINA_INLIST_FOREACH(found_par->lines, ln)
8103 if (ln->par->y + ln->y > y) break;
8104 if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
8106 evas_textblock_cursor_line_set(cur, ln->par->line_no +
8108 return ln->par->line_no + ln->line_no;
8112 else if (o->paragraphs && (y >= o->paragraphs->y + o->formatted.h))
8115 /* If we are after the last paragraph, use the last position in the
8117 evas_textblock_cursor_paragraph_last(cur);
8118 if (cur->node && cur->node->par)
8120 line_no = cur->node->par->line_no;
8121 if (cur->node->par->lines)
8123 line_no += ((Evas_Object_Textblock_Line *)
8124 EINA_INLIST_GET(cur->node->par->lines)->last)->line_no;
8129 else if (o->paragraphs && (y < o->paragraphs->y))
8132 evas_textblock_cursor_paragraph_first(cur);
8133 if (cur->node && cur->node->par)
8135 line_no = cur->node->par->line_no;
8144 * Updates x and w according to the text direction, position in text and
8145 * if it's a special case switch
8147 * @param ti the text item we are working on
8148 * @param x the current x (we get) and the x we return
8149 * @param w the current w (we get) and the w we return
8150 * @param start if this is the first item or not
8151 * @param switch_items toogles item switching (rtl cases)
8154 _evas_textblock_range_calc_x_w(const Evas_Object_Textblock_Item *it,
8155 Evas_Coord *x, Evas_Coord *w, Eina_Bool start, Eina_Bool switch_items)
8157 if ((start && !switch_items) || (!start && switch_items))
8160 if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8161 _ITEM_TEXT(it)->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8163 ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8164 _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
8178 if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8179 _ITEM_TEXT(it)->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8181 ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8182 _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
8199 * Returns the geometry of the range in line ln. Cur1 is the start cursor,
8200 * cur2 is the end cursor, NULL means from the start or to the end accordingly.
8201 * Assumes that ln is valid, and that at least one of cur1 and cur2 is not NULL.
8203 * @param ln the line to work on.
8204 * @param cur1 the start cursor
8205 * @param cur2 the end cursor
8206 * @return Returns the geometry of the range
8209 _evas_textblock_cursor_range_in_line_geometry_get(
8210 const Evas_Object_Textblock_Line *ln, const Evas_Textblock_Cursor *cur1,
8211 const Evas_Textblock_Cursor *cur2)
8213 Evas_Object_Textblock_Item *it;
8214 Evas_Object_Textblock_Item *it1, *it2;
8215 Eina_List *rects = NULL;
8216 Evas_Textblock_Rectangle *tr;
8218 Eina_Bool switch_items;
8219 const Evas_Textblock_Cursor *cur;
8221 cur = (cur1) ? cur1 : cur2;
8223 /* Find the first and last items */
8226 EINA_INLIST_FOREACH(ln->items, it)
8229 item_len = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
8230 _ITEM_TEXT(it)->text_props.text_len
8232 if ((!cur1 || (cur1->pos < it->text_pos + item_len)) &&
8233 (!cur2 || (cur2->pos >= it->text_pos)))
8238 start = item_len; /* start stores the first item_len */
8241 end = item_len; /* end stores the last item_len */
8245 /* If we couldn't find even one item, return */
8246 if (!it1) return NULL;
8248 /* If the first item is logically before or equal the second item
8249 * we have to set start and end differently than in the other case */
8250 if (it1->text_pos <= it2->text_pos)
8252 start = (cur1) ? (cur1->pos - it1->text_pos) : 0;
8253 end = (cur2) ? (cur2->pos - it2->text_pos) : end;
8254 switch_items = EINA_FALSE;
8258 start = (cur2) ? (cur2->pos - it1->text_pos) : start;
8259 end = (cur1) ? (cur1->pos - it2->text_pos) : 0;
8260 switch_items = EINA_TRUE;
8263 /* IMPORTANT: Don't use cur1/cur2 past this point (because they probably
8264 * don't make sense anymore. That's why there are start and end),
8265 * unless you know what you are doing */
8267 /* Special case when they share the same item and it's a text item */
8268 if ((it1 == it2) && (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT))
8270 Evas_Coord x1, w1, x2, w2;
8271 Evas_Coord x, w, y, h;
8272 Evas_Object_Textblock_Text_Item *ti;
8275 ti = _ITEM_TEXT(it1);
8276 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8277 ti->parent.format->font.font,
8285 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8286 ti->parent.format->font.font,
8295 /* Make x2 the one on the right */
8309 if (ti->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8322 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8323 rects = eina_list_append(rects, tr);
8324 tr->x = ln->x + it1->x + x;
8325 tr->y = ln->par->y + ln->y;
8330 else if (it1 != it2)
8332 /* Get the middle items */
8333 Evas_Coord min_x, max_x;
8335 it = _ITEM(EINA_INLIST_GET(it1)->next);
8336 min_x = max_x = it->x;
8338 if (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8341 Evas_Object_Textblock_Text_Item *ti;
8343 ti = _ITEM_TEXT(it1);
8345 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8346 ti->parent.format->font.font,
8352 /* BUG! Skip the first item */
8357 _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
8365 _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
8370 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8371 rects = eina_list_append(rects, tr);
8372 tr->x = ln->x + it1->x + x;
8373 tr->y = ln->par->y + ln->y;
8378 while (it && (it != it2))
8380 max_x = it->x + it->adv;
8381 it = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(it)->next;
8385 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8386 rects = eina_list_append(rects, tr);
8387 tr->x = ln->x + min_x;
8388 tr->y = ln->par->y + ln->y;
8390 tr->w = max_x - min_x;
8392 if (it2->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8395 Evas_Object_Textblock_Text_Item *ti;
8397 ti = _ITEM_TEXT(it2);
8399 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8400 ti->parent.format->font.font,
8406 /* BUG! skip the last item */
8411 _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
8419 _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
8424 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8425 rects = eina_list_append(rects, tr);
8426 tr->x = ln->x + it2->x + x;
8427 tr->y = ln->par->y + ln->y;
8435 evas_textblock_cursor_range_geometry_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
8437 Evas_Object_Textblock *o;
8438 Evas_Object_Textblock_Line *ln1, *ln2;
8439 Evas_Object_Textblock_Item *it1, *it2;
8440 Eina_List *rects = NULL;
8441 Evas_Textblock_Rectangle *tr;
8443 if (!cur1 || !cur1->node) return NULL;
8444 if (!cur2 || !cur2->node) return NULL;
8445 if (cur1->obj != cur2->obj) return NULL;
8446 o = (Evas_Object_Textblock *)(cur1->obj->object_data);
8447 if (!o->formatted.valid) _relayout(cur1->obj);
8448 if (evas_textblock_cursor_compare(cur1, cur2) > 0)
8450 const Evas_Textblock_Cursor *tc;
8459 _find_layout_item_match(cur1, &ln1, &it1);
8460 if (!ln1 || !it1) return NULL;
8461 _find_layout_item_match(cur2, &ln2, &it2);
8462 if (!ln2 || !it2) return NULL;
8466 rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
8471 Evas_Object_Textblock_Line *plni, *lni;
8472 Eina_List *rects2 = NULL;
8473 /* Handle the first line */
8474 rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
8477 /* Handle the lines between the first and the last line */
8478 lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(ln1)->next;
8479 if (!lni && (ln1->par != ln2->par))
8481 lni = ((Evas_Object_Textblock_Paragraph *)
8482 EINA_INLIST_GET(ln1->par)->next)->lines;
8484 while (lni && (lni != ln2))
8486 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8487 rects = eina_list_append(rects, tr);
8489 tr->y = lni->par->y + lni->y;
8493 lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(lni)->next;
8494 if (!lni && (plni->par != ln2->par))
8496 lni = ((Evas_Object_Textblock_Paragraph *)
8497 EINA_INLIST_GET(plni->par)->next)->lines;
8500 rects2 = _evas_textblock_cursor_range_in_line_geometry_get(ln2,
8502 rects = eina_list_merge(rects, rects2);
8508 evas_textblock_cursor_format_item_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8510 Evas_Object_Textblock *o;
8511 Evas_Object_Textblock_Line *ln = NULL;
8512 Evas_Object_Textblock_Format_Item *fi;
8513 Evas_Object_Textblock_Item *it = NULL;
8514 Evas_Coord x, y, w, h;
8516 if (!cur || !evas_textblock_cursor_format_is_visible_get(cur)) return EINA_FALSE;
8517 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8518 if (!o->formatted.valid) _relayout(cur->obj);
8519 if (!evas_textblock_cursor_format_is_visible_get(cur)) return EINA_FALSE;
8520 _find_layout_item_line_match(cur->obj, cur->node, cur->pos, &ln, &it);
8521 fi = _ITEM_FORMAT(it);
8522 if ((!ln) || (!fi)) return EINA_FALSE;
8523 x = ln->x + fi->parent.x;
8524 y = ln->par->y + ln->y + ln->baseline + fi->y;
8535 evas_textblock_cursor_eol_get(const Evas_Textblock_Cursor *cur)
8537 Eina_Bool ret = EINA_FALSE;
8538 Evas_Textblock_Cursor cur2;
8539 if (!cur) return EINA_FALSE;
8541 cur2.obj = cur->obj;
8542 evas_textblock_cursor_copy(cur, &cur2);
8543 evas_textblock_cursor_line_char_last(&cur2);
8544 if (cur2.pos == cur->pos)
8551 /* general controls */
8553 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)
8555 Evas_Object_Textblock_Line *ln;
8558 ln = _find_layout_line_num(obj, line);
8559 if (!ln) return EINA_FALSE;
8560 if (cx) *cx = ln->x;
8561 if (cy) *cy = ln->par->y + ln->y;
8562 if (cw) *cw = ln->w;
8563 if (ch) *ch = ln->h;
8568 evas_object_textblock_clear(Evas_Object *obj)
8571 Evas_Textblock_Cursor *cur;
8576 _paragraphs_free(obj, o->paragraphs);
8577 o->paragraphs = NULL;
8581 o->cursor->node = NULL;
8583 EINA_LIST_FOREACH(o->cursors, l, cur)
8589 _evas_textblock_changed(o, obj);
8593 evas_object_textblock_size_formatted_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
8596 if (!o->formatted.valid) _relayout(obj);
8597 if (w) *w = o->formatted.w;
8598 if (h) *h = o->formatted.h;
8602 _size_native_calc_line_finalize(const Evas_Object *obj, Eina_List *items,
8603 Evas_Coord *ascent, Evas_Coord *descent, Evas_Coord *w)
8605 Evas_Object_Textblock_Item *it;
8608 it = eina_list_data_get(items);
8609 /* If there are no text items yet, calc ascent/descent
8610 * according to the current format. */
8611 if (it && (*ascent + *descent == 0))
8612 _layout_format_ascent_descent_adjust(obj, ascent, descent, it->format);
8615 /* Adjust all the item sizes according to the final line size,
8616 * and update the x positions of all the items of the line. */
8617 EINA_LIST_FOREACH(items, i, it)
8619 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
8621 Evas_Coord fw, fh, fy;
8623 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
8624 if (!fi->formatme) goto loop_advance;
8625 _layout_calculate_format_item_size(obj, fi, ascent,
8626 descent, &fy, &fw, &fh);
8636 _size_native_calc_paragraph_size(const Evas_Object *obj,
8637 const Evas_Object_Textblock *o,
8638 const Evas_Object_Textblock_Paragraph *par,
8639 Evas_Coord *_w, Evas_Coord *_h)
8642 Evas_Object_Textblock_Item *it;
8643 Eina_List *line_items = NULL;
8644 Evas_Coord w = 0, y = 0, wmax = 0, h = 0, ascent = 0, descent = 0;
8646 EINA_LIST_FOREACH(par->logical_items, i, it)
8648 line_items = eina_list_append(line_items, it);
8649 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
8651 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
8652 if (fi->item && (_IS_LINE_SEPARATOR(fi->item) ||
8653 _IS_PARAGRAPH_SEPARATOR(o, fi->item)))
8655 _size_native_calc_line_finalize(obj, line_items, &ascent,
8658 if (ascent + descent > h)
8659 h = ascent + descent;
8665 ascent = descent = 0;
8666 line_items = eina_list_free(line_items);
8670 Evas_Coord fw, fh, fy;
8671 /* If there are no text items yet, calc ascent/descent
8672 * according to the current format. */
8673 if (it && (ascent + descent == 0))
8674 _layout_format_ascent_descent_adjust(obj, &ascent,
8675 &descent, it->format);
8677 _layout_calculate_format_item_size(obj, fi, &ascent,
8678 &descent, &fy, &fw, &fh);
8683 _layout_format_ascent_descent_adjust(obj, &ascent,
8684 &descent, it->format);
8688 _size_native_calc_line_finalize(obj, line_items, &ascent, &descent, &w);
8690 line_items = eina_list_free(line_items);
8692 /* Do the last addition */
8693 if (ascent + descent > h)
8694 h = ascent + descent;
8704 evas_object_textblock_size_native_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
8707 if (!o->native.valid)
8709 Evas_Coord wmax = 0, hmax = 0;
8710 Evas_Object_Textblock_Paragraph *par;
8711 /* We just want the layout objects to update, should probably
8713 if (!o->formatted.valid) _relayout(obj);
8714 EINA_INLIST_FOREACH(o->paragraphs, par)
8717 _size_native_calc_paragraph_size(obj, o, par, &tw, &th);
8726 o->native.valid = 1;
8727 o->content_changed = 0;
8728 o->format_changed = EINA_FALSE;
8730 if (w) *w = o->native.w;
8731 if (h) *h = o->native.h;
8735 evas_object_textblock_style_insets_get(const Evas_Object *obj, Evas_Coord *l, Evas_Coord *r, Evas_Coord *t, Evas_Coord *b)
8738 if (!o->formatted.valid) _relayout(obj);
8739 if (l) *l = o->style_pad.l;
8740 if (r) *r = o->style_pad.r;
8741 if (t) *t = o->style_pad.t;
8742 if (b) *b = o->style_pad.b;
8745 /* all nice and private */
8747 evas_object_textblock_init(Evas_Object *obj)
8749 Evas_Object_Textblock *o;
8750 #ifdef HAVE_LINEBREAK
8751 static Eina_Bool linebreak_init = EINA_FALSE;
8752 if (!linebreak_init)
8754 linebreak_init = EINA_TRUE;
8759 /* alloc image ob, setup methods and default values */
8760 obj->object_data = evas_object_textblock_new();
8761 /* set up default settings for this kind of object */
8762 obj->cur.color.r = 255;
8763 obj->cur.color.g = 255;
8764 obj->cur.color.b = 255;
8765 obj->cur.color.a = 255;
8766 obj->cur.geometry.x = 0.0;
8767 obj->cur.geometry.y = 0.0;
8768 obj->cur.geometry.w = 0.0;
8769 obj->cur.geometry.h = 0.0;
8771 /* set up object-specific settings */
8772 obj->prev = obj->cur;
8773 /* set up methods (compulsory) */
8774 obj->func = &object_func;
8777 o = (Evas_Object_Textblock *)(obj->object_data);
8778 o->cursor->obj = obj;
8779 o->legacy_newline = EINA_TRUE;
8783 evas_object_textblock_new(void)
8785 Evas_Object_Textblock *o;
8787 /* alloc obj private data */
8788 EVAS_MEMPOOL_INIT(_mp_obj, "evas_object_textblock", Evas_Object_Textblock, 64, NULL);
8789 o = EVAS_MEMPOOL_ALLOC(_mp_obj, Evas_Object_Textblock);
8790 if (!o) return NULL;
8791 EVAS_MEMPOOL_PREP(_mp_obj, o, Evas_Object_Textblock);
8792 o->magic = MAGIC_OBJ_TEXTBLOCK;
8793 o->cursor = calloc(1, sizeof(Evas_Textblock_Cursor));
8794 _format_command_init();
8799 evas_object_textblock_free(Evas_Object *obj)
8801 Evas_Object_Textblock *o;
8803 evas_object_textblock_clear(obj);
8804 evas_object_textblock_style_set(obj, NULL);
8805 o = (Evas_Object_Textblock *)(obj->object_data);
8809 Evas_Textblock_Cursor *cur;
8811 cur = (Evas_Textblock_Cursor *)o->cursors->data;
8812 o->cursors = eina_list_remove_list(o->cursors, o->cursors);
8815 if (o->repch) eina_stringshare_del(o->repch);
8816 if (o->ellip_ti) _item_free(obj, NULL, _ITEM(o->ellip_ti));
8818 EVAS_MEMPOOL_FREE(_mp_obj, o);
8819 _format_command_shutdown();
8824 evas_object_textblock_render(Evas_Object *obj, void *output, void *context, void *surface, int x, int y)
8826 Evas_Object_Textblock_Paragraph *par, *start = NULL;
8827 Evas_Object_Textblock_Line *ln;
8828 Evas_Object_Textblock *o;
8830 unsigned char r = 0, g = 0, b = 0, a = 0;
8831 unsigned char r2 = 0, g2 = 0, b2 = 0, a2 = 0;
8832 unsigned char r3 = 0, g3 = 0, b3 = 0, a3 = 0;
8833 int cx, cy, cw, ch, clip;
8834 const char vals[5][5] =
8843 /* render object to surface with context, and offxet by x,y */
8844 o = (Evas_Object_Textblock *)(obj->object_data);
8845 obj->layer->evas->engine.func->context_multiplier_unset(output,
8847 /* FIXME: This clipping is just until we fix inset handling correctly. */
8848 ENFN->context_clip_clip(output, context,
8849 obj->cur.geometry.x + x,
8850 obj->cur.geometry.y + y,
8851 obj->cur.geometry.w,
8852 obj->cur.geometry.h);
8853 clip = ENFN->context_clip_get(output, context, &cx, &cy, &cw, &ch);
8854 /* If there are no paragraphs and thus there are no lines,
8855 * there's nothing left to do. */
8856 if (!o->paragraphs) return;
8858 #define ITEM_WALK() \
8859 EINA_INLIST_FOREACH(start, par) \
8861 if (!par->visible) continue; \
8864 if ((obj->cur.geometry.y + y + par->y + par->h) < (cy - 20)) \
8866 if ((obj->cur.geometry.y + y + par->y) > (cy + ch + 20)) \
8869 _layout_paragraph_render(o, par); \
8870 EINA_INLIST_FOREACH(par->lines, ln) \
8872 Evas_Object_Textblock_Item *itr; \
8876 if ((obj->cur.geometry.y + y + par->y + ln->y + ln->h) < (cy - 20)) \
8878 if ((obj->cur.geometry.y + y + par->y + ln->y) > (cy + ch + 20)) \
8881 EINA_INLIST_FOREACH(ln->items, itr) \
8884 yoff = ln->baseline; \
8885 if (itr->format->valign != -1.0) \
8887 yoff += itr->format->valign * (ln->h - itr->h); \
8891 if ((obj->cur.geometry.x + x + ln->x + itr->x + itr->w) < (cx - 20)) \
8893 if ((obj->cur.geometry.x + x + ln->x + itr->x) > (cx + cw + 20)) \
8896 if ((ln->x + itr->x + itr->w) <= 0) continue; \
8897 if (ln->x + itr->x > obj->cur.geometry.w) break; \
8900 #define ITEM_WALK_END() \
8906 #define COLOR_SET(col) \
8907 ENFN->context_color_set(output, context, \
8908 (obj->cur.cache.clip.r * ti->parent.format->color.col.r) / 255, \
8909 (obj->cur.cache.clip.g * ti->parent.format->color.col.g) / 255, \
8910 (obj->cur.cache.clip.b * ti->parent.format->color.col.b) / 255, \
8911 (obj->cur.cache.clip.a * ti->parent.format->color.col.a) / 255);
8912 #define COLOR_SET_AMUL(col, amul) \
8913 ENFN->context_color_set(output, context, \
8914 (obj->cur.cache.clip.r * ti->parent.format->color.col.r * (amul)) / 65025, \
8915 (obj->cur.cache.clip.g * ti->parent.format->color.col.g * (amul)) / 65025, \
8916 (obj->cur.cache.clip.b * ti->parent.format->color.col.b * (amul)) / 65025, \
8917 (obj->cur.cache.clip.a * ti->parent.format->color.col.a * (amul)) / 65025);
8918 #define DRAW_TEXT(ox, oy) \
8919 if (ti->parent.format->font.font) ENFN->font_draw(output, context, surface, ti->parent.format->font.font, \
8920 obj->cur.geometry.x + ln->x + ti->parent.x + x + (ox), \
8921 obj->cur.geometry.y + ln->par->y + ln->y + yoff + y + (oy), \
8922 ti->parent.w, ti->parent.h, ti->parent.w, ti->parent.h, \
8926 #define DRAW_RECT(ox, oy, ow, oh, or, og, ob, oa) \
8929 ENFN->context_color_set(output, \
8931 (obj->cur.cache.clip.r * or) / 255, \
8932 (obj->cur.cache.clip.g * og) / 255, \
8933 (obj->cur.cache.clip.b * ob) / 255, \
8934 (obj->cur.cache.clip.a * oa) / 255); \
8935 ENFN->rectangle_draw(output, \
8938 obj->cur.geometry.x + ln->x + x + (ox), \
8939 obj->cur.geometry.y + ln->par->y + ln->y + y + (oy), \
8945 #define DRAW_FORMAT(oname, oy, oh, or, og, ob, oa) \
8948 if (itr->format->oname) \
8950 or = itr->format->color.oname.r; \
8951 og = itr->format->color.oname.g; \
8952 ob = itr->format->color.oname.b; \
8953 oa = itr->format->color.oname.a; \
8954 if (!EINA_INLIST_GET(itr)->next) \
8956 DRAW_RECT(itr->x, oy, itr->w, oh, or, og, ob, oa); \
8960 DRAW_RECT(itr->x, oy, itr->adv, oh, or, og, ob, oa); \
8967 Evas_Coord look_for_y = 0 - (obj->cur.geometry.y + y);
8970 Evas_Coord tmp_lfy = cy - (obj->cur.geometry.y + y);
8971 if (tmp_lfy > look_for_y)
8972 look_for_y = tmp_lfy;
8975 if (look_for_y >= 0)
8976 start = _layout_find_paragraph_by_y(o, look_for_y);
8979 start = o->paragraphs;
8984 DRAW_FORMAT(backing, 0, ln->h, r, g, b, a);
8988 /* There are size adjustments that depend on the styles drawn here back
8989 * in "_text_item_update_sizes" should not modify one without the other. */
8991 /* prepare everything for text draw */
8996 int shad_dst, shad_sz, dx, dy, haveshad;
8997 Evas_Object_Textblock_Text_Item *ti;
8998 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9001 shad_dst = shad_sz = dx = dy = haveshad = 0;
9002 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC)
9004 case EVAS_TEXT_STYLE_SHADOW:
9005 case EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW:
9009 case EVAS_TEXT_STYLE_OUTLINE_SHADOW:
9010 case EVAS_TEXT_STYLE_FAR_SHADOW:
9014 case EVAS_TEXT_STYLE_FAR_SOFT_SHADOW:
9019 case EVAS_TEXT_STYLE_SOFT_SHADOW:
9031 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_SHADOW_DIRECTION)
9033 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT:
9037 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM:
9041 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT:
9045 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT:
9049 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT:
9053 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP:
9057 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT:
9061 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT:
9077 for (j = 0; j < 5; j++)
9079 for (i = 0; i < 5; i++)
9081 if (vals[i][j] != 0)
9083 COLOR_SET_AMUL(shadow, vals[i][j] * 50);
9084 DRAW_TEXT(i - 2 + dx, j - 2 + dy);
9099 Evas_Object_Textblock_Text_Item *ti;
9100 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9103 if (ti->parent.format->style == EVAS_TEXT_STYLE_GLOW)
9105 for (j = 0; j < 5; j++)
9107 for (i = 0; i < 5; i++)
9109 if (vals[i][j] != 0)
9111 COLOR_SET_AMUL(glow, vals[i][j] * 50);
9112 DRAW_TEXT(i - 2, j - 2);
9128 Evas_Object_Textblock_Text_Item *ti;
9129 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9132 if ((ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE) ||
9133 (ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE_SHADOW) ||
9134 (ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW))
9142 else if (ti->parent.format->style == EVAS_TEXT_STYLE_SOFT_OUTLINE)
9144 for (j = 0; j < 5; j++)
9146 for (i = 0; i < 5; i++)
9148 if (((i != 2) || (j != 2)) && (vals[i][j] != 0))
9150 COLOR_SET_AMUL(outline, vals[i][j] * 50);
9151 DRAW_TEXT(i - 2, j - 2);
9159 /* normal text and lines */
9162 Evas_Object_Textblock_Text_Item *ti;
9163 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9172 DRAW_FORMAT(strikethrough, (ln->h / 2), 1, r, g, b, a);
9175 DRAW_FORMAT(underline, ln->baseline + 1, 1, r2, g2, b2, a2);
9178 DRAW_FORMAT(underline2, ln->baseline + 3, 1, r3, g3, b3, a3);
9184 evas_object_textblock_render_pre(Evas_Object *obj)
9186 Evas_Object_Textblock *o;
9189 /* dont pre-render the obj twice! */
9190 if (obj->pre_render_done) return;
9191 obj->pre_render_done = 1;
9192 /* pre-render phase. this does anything an object needs to do just before */
9193 /* rendering. this could mean loading the image data, retrieving it from */
9194 /* elsewhere, decoding video etc. */
9195 /* then when this is done the object needs to figure if it changed and */
9196 /* if so what and where and add the appropriate redraw textblocks */
9197 o = (Evas_Object_Textblock *)(obj->object_data);
9198 if ((o->changed) || (o->content_changed) || (o->format_changed) ||
9199 ((obj->cur.geometry.w != o->last_w) ||
9200 (((o->valign != 0.0) || (o->have_ellipsis)) &&
9201 (obj->cur.geometry.h != o->last_h))))
9205 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9206 is_v = evas_object_is_visible(obj);
9207 was_v = evas_object_was_visible(obj);
9213 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9214 is_v = evas_object_is_visible(obj);
9215 was_v = evas_object_was_visible(obj);
9218 /* if someone is clipping this obj - go calculate the clipper */
9219 if (obj->cur.clipper)
9221 if (obj->cur.cache.clip.dirty)
9222 evas_object_clip_recalc(obj->cur.clipper);
9223 obj->cur.clipper->func->render_pre(obj->cur.clipper);
9225 /* now figure what changed and add draw rects */
9226 /* if it just became visible or invisible */
9227 is_v = evas_object_is_visible(obj);
9228 was_v = evas_object_was_visible(obj);
9231 evas_object_render_pre_visible_change(&obj->layer->evas->clip_changes, obj, is_v, was_v);
9234 if ((obj->cur.map != obj->prev.map) ||
9235 (obj->cur.usemap != obj->prev.usemap))
9237 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9240 /* it's not visible - we accounted for it appearing or not so just abort */
9241 if (!is_v) goto done;
9242 /* clipper changed this is in addition to anything else for obj */
9243 evas_object_render_pre_clipper_change(&obj->layer->evas->clip_changes, obj);
9244 /* if we restacked (layer or just within a layer) and don't clip anyone */
9247 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9250 /* if it changed color */
9251 if ((obj->cur.color.r != obj->prev.color.r) ||
9252 (obj->cur.color.g != obj->prev.color.g) ||
9253 (obj->cur.color.b != obj->prev.color.b) ||
9254 (obj->cur.color.a != obj->prev.color.a))
9256 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9259 /* if it changed geometry - and obviously not visibility or color */
9260 /* calculate differences since we have a constant color fill */
9261 /* we really only need to update the differences */
9262 if ((obj->cur.geometry.x != obj->prev.geometry.x) ||
9263 (obj->cur.geometry.y != obj->prev.geometry.y) ||
9264 (obj->cur.geometry.w != obj->prev.geometry.w) ||
9265 (obj->cur.geometry.h != obj->prev.geometry.h))
9267 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9271 evas_object_render_pre_effect_updates(&obj->layer->evas->clip_changes, obj, is_v, was_v);
9275 evas_object_textblock_render_post(Evas_Object *obj)
9277 /* Evas_Object_Textblock *o; */
9279 /* this moves the current data to the previous state parts of the object */
9280 /* in whatever way is safest for the object. also if we don't need object */
9281 /* data anymore we can free it if the object deems this is a good idea */
9282 /* o = (Evas_Object_Textblock *)(obj->object_data); */
9283 /* remove those pesky changes */
9284 evas_object_clip_changes_clean(obj);
9285 /* move cur to prev safely for object data */
9286 obj->prev = obj->cur;
9287 /* o->prev = o->cur; */
9288 /* o->changed = 0; */
9291 static unsigned int evas_object_textblock_id_get(Evas_Object *obj)
9293 Evas_Object_Textblock *o;
9295 o = (Evas_Object_Textblock *)(obj->object_data);
9297 return MAGIC_OBJ_TEXTBLOCK;
9300 static unsigned int evas_object_textblock_visual_id_get(Evas_Object *obj)
9302 Evas_Object_Textblock *o;
9304 o = (Evas_Object_Textblock *)(obj->object_data);
9306 return MAGIC_OBJ_CUSTOM;
9309 static void *evas_object_textblock_engine_data_get(Evas_Object *obj)
9311 Evas_Object_Textblock *o;
9313 o = (Evas_Object_Textblock *)(obj->object_data);
9314 if (!o) return NULL;
9315 return o->engine_data;
9319 evas_object_textblock_is_opaque(Evas_Object *obj __UNUSED__)
9321 /* this returns 1 if the internal object data implies that the object is */
9322 /* currently fulyl opque over the entire gradient it occupies */
9327 evas_object_textblock_was_opaque(Evas_Object *obj __UNUSED__)
9329 /* this returns 1 if the internal object data implies that the object was */
9330 /* currently fulyl opque over the entire gradient it occupies */
9335 evas_object_textblock_coords_recalc(Evas_Object *obj)
9337 Evas_Object_Textblock *o;
9339 o = (Evas_Object_Textblock *)(obj->object_data);
9340 if ((obj->cur.geometry.w != o->last_w) ||
9341 (((o->valign != 0.0) || (o->have_ellipsis)) &&
9342 (obj->cur.geometry.h != o->last_h)))
9344 o->formatted.valid = 0;
9350 evas_object_textblock_scale_update(Evas_Object *obj)
9352 Evas_Object_Textblock *o;
9354 o = (Evas_Object_Textblock *)(obj->object_data);
9355 _evas_textblock_invalidate_all(o);
9356 _evas_textblock_changed(o, obj);
9360 _evas_object_textblock_rehint(Evas_Object *obj)
9362 Evas_Object_Textblock *o;
9363 Evas_Object_Textblock_Paragraph *par;
9364 Evas_Object_Textblock_Line *ln;
9366 o = (Evas_Object_Textblock *)(obj->object_data);
9367 EINA_INLIST_FOREACH(o->paragraphs, par)
9369 EINA_INLIST_FOREACH(par->lines, ln)
9371 Evas_Object_Textblock_Item *it;
9373 EINA_INLIST_FOREACH(ln->items, it)
9375 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
9377 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
9378 if (ti->parent.format->font.font)
9380 #ifdef EVAS_FRAME_QUEUING
9381 evas_common_pipe_op_text_flush((RGBA_Font *) ti->parent.format->font.font);
9383 evas_font_load_hinting_set(obj->layer->evas,
9384 ti->parent.format->font.font,
9385 obj->layer->evas->hinting);
9391 _evas_textblock_invalidate_all(o);
9392 _evas_textblock_changed(o, obj);
9400 /* return EINA_FALSE on error, used in unit_testing */
9402 _evas_textblock_check_item_node_link(Evas_Object *obj)
9404 Evas_Object_Textblock *o;
9405 Evas_Object_Textblock_Paragraph *par;
9406 Evas_Object_Textblock_Line *ln;
9407 Evas_Object_Textblock_Item *it;
9409 o = (Evas_Object_Textblock *)(obj->object_data);
9410 if (!o) return EINA_FALSE;
9412 if (!o->formatted.valid) _relayout(obj);
9414 EINA_INLIST_FOREACH(o->paragraphs, par)
9416 EINA_INLIST_FOREACH(par->lines, ln)
9418 EINA_INLIST_FOREACH(ln->items, it)
9420 if (it->text_node != par->text_node)
9429 _evas_textblock_format_offset_get(const Evas_Object_Textblock_Node_Format *n)
9436 /* Good for debugging */
9438 pfnode(Evas_Object_Textblock_Node_Format *n)
9440 printf("Format Node: %p\n", n);
9441 printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
9442 printf("text_node = %p, offset = %u, visible = %d\n", n->text_node, n->offset, n->visible);
9443 printf("'%s'\n", eina_strbuf_string_get(n->format));
9447 ptnode(Evas_Object_Textblock_Node_Text *n)
9449 printf("Text Node: %p\n", n);
9450 printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
9451 printf("format_node = %p\n", n->format_node);
9452 printf("'%ls'\n", eina_ustrbuf_string_get(n->unicode));
9456 pitem(Evas_Object_Textblock_Item *it)
9458 Evas_Object_Textblock_Text_Item *ti;
9459 Evas_Object_Textblock_Format_Item *fi;
9460 printf("Item: %p\n", it);
9461 printf("Type: %s (%d)\n", (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
9462 "TEXT" : "FORMAT", it->type);
9463 printf("Text pos: %d Visual pos: %d\n", it->text_pos,
9470 printf("Coords: x = %d w = %d adv = %d\n", (int) it->x, (int) it->w,
9472 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
9474 ti = _ITEM_TEXT(it);
9475 printf("Text: '%*ls'\n", ti->text_props.text_len, GET_ITEM_TEXT(ti));
9479 fi = _ITEM_FORMAT(it);
9480 printf("Format: '%s'\n", fi->item);
9485 ppar(Evas_Object_Textblock_Paragraph *par)
9487 Evas_Object_Textblock_Item *it;
9489 EINA_LIST_FOREACH(par->logical_items, i, it)
9491 printf("***********************\n");