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;
221 Eina_Bool is_new : 1;
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;
234 Eina_Bool is_new : 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->is_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;
3533 if (c->par->text_node)
3535 /* Skip this paragraph if width is the same, there is no ellipsis
3536 * and we aren't just calculating. */
3537 if (!c->par->text_node->is_new && !c->par->text_node->dirty &&
3538 !c->width_changed && c->par->lines &&
3539 !c->o->have_ellipsis)
3541 Evas_Object_Textblock_Line *ln;
3542 /* Update c->line_no */
3543 ln = (Evas_Object_Textblock_Line *)
3544 EINA_INLIST_GET(c->par->lines)->last;
3546 c->line_no = c->par->line_no + ln->line_no + 1;
3549 c->par->text_node->dirty = EINA_FALSE;
3550 c->par->text_node->is_new = EINA_FALSE;
3551 c->par->rendered = EINA_FALSE;
3553 /* Merge back and clear the paragraph */
3555 Eina_List *itr, *itr_next;
3556 Evas_Object_Textblock_Item *ititr, *prev_it = NULL;
3557 _paragraph_clear(c->obj, c->par);
3558 EINA_LIST_FOREACH_SAFE(c->par->logical_items, itr, itr_next, ititr)
3560 if (ititr->merge && prev_it &&
3561 (prev_it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
3562 (ititr->type == EVAS_TEXTBLOCK_ITEM_TEXT))
3564 _layout_item_merge_and_free(c, _ITEM_TEXT(prev_it),
3566 c->par->logical_items =
3567 eina_list_remove_list(c->par->logical_items, itr);
3579 it = _ITEM(eina_list_data_get(c->par->logical_items));
3580 _layout_line_new(c, it->format);
3581 /* We walk on our own because we want to be able to add items from
3582 * inside the list and then walk them on the next iteration. */
3583 for (i = c->par->logical_items ; i ; )
3587 it = _ITEM(eina_list_data_get(i));
3588 /* Skip visually deleted items */
3589 if (it->visually_deleted)
3591 i = eina_list_next(i);
3595 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3597 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
3598 _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
3599 &c->maxdescent, ti->parent.format);
3603 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
3606 /* If there are no text items yet, calc ascent/descent
3607 * according to the current format. */
3608 if (c->maxascent + c->maxdescent == 0)
3609 _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
3610 &c->maxdescent, it->format);
3612 _layout_calculate_format_item_size(c->obj, fi, &c->maxascent,
3613 &c->maxdescent, &fi->y, &fi->parent.w, &fi->parent.h);
3614 fi->parent.adv = fi->parent.w;
3619 /* Check if we need to wrap, i.e the text is bigger than the width,
3620 or we already found a wrap point. */
3622 (((c->x + it->adv) >
3623 (c->w - c->o->style_pad.l - c->o->style_pad.r -
3624 c->marginl - c->marginr)) || (wrap > 0)))
3626 /* Handle ellipsis here. If we don't have more width left
3627 * and no height left, or no more width left and no wrapping. */
3628 if ((it->format->ellipsis == 1.0) && (c->h >= 0) &&
3629 ((2 * it->h + c->y >
3630 c->h - c->o->style_pad.t - c->o->style_pad.b) ||
3631 (!it->format->wrap_word && !it->format->wrap_char &&
3632 !it->format->wrap_mixed)))
3634 _layout_handle_ellipsis(c, it, i);
3638 /* If we want to wrap and it's worth checking for wrapping
3639 * (i.e there's actually text). */
3640 else if ((it->format->wrap_word || it->format->wrap_char ||
3641 it->format->wrap_mixed) && it->text_node)
3643 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3645 /* Don't wrap if it's the only item */
3648 /*FIXME: I should handle format correctly,
3649 i.e verify we are allowed to break here */
3650 _layout_line_advance(c, it->format);
3656 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
3659 #ifdef HAVE_LINEBREAK
3660 /* If we haven't calculated the linebreaks yet,
3664 /* Only relevant in those cases */
3665 if (it->format->wrap_word || it->format->wrap_mixed)
3668 lang = (it->format->font.fdesc) ?
3669 it->format->font.fdesc->lang : "";
3671 eina_ustrbuf_length_get(
3672 it->text_node->unicode);
3673 line_breaks = malloc(len);
3674 set_linebreaks_utf32((const utf32_t *)
3675 eina_ustrbuf_string_get(
3676 it->text_node->unicode),
3677 len, lang, line_breaks);
3682 line_start = c->ln->items->text_pos;
3684 line_start = ti->parent.text_pos;
3687 /* If we don't already have a wrap point from before */
3690 if (it->format->wrap_word)
3691 wrap = _layout_get_wordwrap(c, it->format, ti,
3692 line_start, line_breaks);
3693 else if (it->format->wrap_char)
3694 wrap = _layout_get_charwrap(c, it->format, ti,
3695 line_start, line_breaks);
3696 else if (it->format->wrap_mixed)
3697 wrap = _layout_get_mixedwrap(c, it->format, ti,
3698 line_start, line_breaks);
3703 /* If it's before the item, rollback and apply.
3704 if it's in the item, cut.
3705 If it's after the item, delay the cut */
3708 size_t uwrap = (size_t) wrap;
3709 if (uwrap < ti->parent.text_pos)
3711 /* Rollback latest additions, and cut that
3713 i = eina_list_prev(i);
3714 it = eina_list_data_get(i);
3715 while (uwrap < it->text_pos)
3717 c->ln->items = _ITEM(
3719 EINA_INLIST_GET(c->ln->items),
3720 EINA_INLIST_GET(it)));
3721 i = eina_list_prev(i);
3722 it = eina_list_data_get(i);
3725 c->ln->items = _ITEM(
3727 EINA_INLIST_GET(c->ln->items),
3728 EINA_INLIST_GET(it)));
3731 /* If it points to the end, it means the previous
3732 * char is a whitespace we should remove, so this
3733 * is a wanted cutting point. */
3734 else if (uwrap > ti->parent.text_pos +
3735 ti->text_props.text_len)
3736 wrap = -1; /* Delay the cut in a smart way
3737 i.e use the item_pos as the line_start, because
3738 there's already no cut before*/
3740 wrap -= ti->parent.text_pos; /* Cut here */
3745 _layout_item_text_split_strip_white(c, ti, i, wrap);
3749 /* Should wrap before the item */
3752 _layout_line_advance(c, it->format);
3760 if (!redo_item && !it->visually_deleted)
3762 c->ln->items = (Evas_Object_Textblock_Item *)
3763 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3764 EINA_INLIST_GET(it));
3765 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3767 Evas_Object_Textblock_Format_Item *fi;
3768 fi = _ITEM_FORMAT(it);
3770 /* If it's a newline, and we are not in newline compat
3771 * mode, or we are in newline compat mode, and this is
3772 * not used as a paragraph separator, advance */
3773 if (fi->item && _IS_LINE_SEPARATOR(fi->item) &&
3774 (!c->o->legacy_newline ||
3781 i = eina_list_next(i);
3785 /* Each line is according to the first item in it, and here
3786 * i is already the next item (or the current if we redo it) */
3789 it = _ITEM(eina_list_data_get(i));
3791 _layout_line_advance(c, it->format);
3796 /* Here 'it' is the last format used */
3797 _layout_line_finalize(c, it->format);
3801 #ifdef HAVE_LINEBREAK
3811 * Invalidate text nodes according to format changes
3812 * This goes through all the new format changes and marks the text nodes
3813 * that should be invalidated because of format changes.
3815 * @param c the working context.
3818 _format_changes_invalidate_text_nodes(Ctxt *c)
3820 Evas_Object_Textblock_Node_Format *fnode = c->o->format_nodes;
3821 Evas_Object_Textblock_Node_Text *start_n = NULL;
3822 Eina_List *fstack = NULL;
3828 const char *fstr = fnode->orig_format;
3829 /* balance < 0 means we gave up and everything should be
3835 start_n = fnode->text_node;
3836 fstack = eina_list_prepend(fstack, fnode);
3838 else if (*fstr == '-')
3843 fstr_len = strlen(fstr);
3844 /* Generic popper, just pop */
3845 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
3847 fstack = eina_list_remove_list(fstack, fstack);
3850 /* Find the matching format and pop it, if the matching format
3851 * is out format, i.e the last one, pop and break. */
3855 Evas_Object_Textblock_Node_Format *fnode2;
3856 EINA_LIST_FOREACH(fstack, i, fnode2)
3858 if (_FORMAT_IS_CLOSER_OF(
3859 fnode2->orig_format, fstr, fstr_len))
3861 fstack = eina_list_remove_list(fstack, i);
3870 Evas_Object_Textblock_Node_Text *f_tnode =
3874 start_n->dirty = EINA_TRUE;
3875 if (start_n == f_tnode)
3878 _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
3883 else if (!fnode->visible)
3888 /* if we don't already have a starting point, use the
3889 * current paragraph. */
3891 start_n = fnode->text_node;
3895 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
3902 start_n->dirty = EINA_TRUE;
3903 start_n = _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
3909 /** FIXME: Document */
3911 _layout_pre(Ctxt *c, int *style_pad_l, int *style_pad_r, int *style_pad_t,
3914 Evas_Object *obj = c->obj;
3915 Evas_Object_Textblock *o = c->o;
3916 /* Mark text nodes as dirty if format have changed. */
3917 if (c->o->format_changed)
3919 _format_changes_invalidate_text_nodes(c);
3922 if (o->content_changed)
3924 Evas_Object_Textblock_Node_Text *n;
3925 c->o->have_ellipsis = 0;
3926 c->par = c->paragraphs = o->paragraphs;
3927 /* Go through all the text nodes to create the logical layout */
3928 EINA_INLIST_FOREACH(c->o->text_nodes, n)
3930 Evas_Object_Textblock_Node_Format *fnode;
3934 /* If it's not a new paragraph, either update it or skip it.
3935 * Remove all the paragraphs that were deleted */
3938 /* Remove all the deleted paragraphs at this point */
3939 while (c->par->text_node != n)
3941 Evas_Object_Textblock_Paragraph *tmp_par =
3942 (Evas_Object_Textblock_Paragraph *)
3943 EINA_INLIST_GET(c->par)->next;
3945 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
3946 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
3947 EINA_INLIST_GET(c->par));
3948 _paragraph_free(obj, c->par);
3953 /* If it's dirty, remove and recreate, if it's clean,
3954 * skip to the next. */
3957 Evas_Object_Textblock_Paragraph *prev_par = c->par;
3959 _layout_paragraph_new(c, n, EINA_TRUE);
3961 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
3962 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
3963 EINA_INLIST_GET(prev_par));
3964 _paragraph_free(obj, prev_par);
3968 c->par = (Evas_Object_Textblock_Paragraph *)
3969 EINA_INLIST_GET(c->par)->next;
3971 /* Update the format stack according to the node's
3973 fnode = n->format_node;
3974 while (fnode && (fnode->text_node == n))
3976 /* Only do this if this actually changes format */
3977 if (fnode->format_change)
3978 _layout_do_format(obj, c, &c->fmt, fnode,
3979 style_pad_l, style_pad_r,
3980 style_pad_t, style_pad_b, EINA_FALSE);
3981 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
3988 /* If it's a new paragraph, just add it. */
3989 _layout_paragraph_new(c, n, EINA_FALSE);
3993 _layout_update_bidi_props(c->o, c->par);
3996 /* For each text node to thorugh all of it's format nodes
3997 * append text from the start to the offset of the next format
3998 * using the last format got. if needed it also creates format
3999 * items this is the core algorithm of the layout mechanism.
4000 * Skip the unicode replacement chars when there are because
4001 * we don't want to print them. */
4002 fnode = n->format_node;
4004 while (fnode && (fnode->text_node == n))
4006 off += fnode->offset;
4007 /* No need to skip on the first run, or a non-visible one */
4008 _layout_text_append(c, c->fmt, n, start, off, o->repch);
4009 _layout_do_format(obj, c, &c->fmt, fnode, style_pad_l,
4010 style_pad_r, style_pad_t, style_pad_b, EINA_TRUE);
4011 if ((c->have_underline2) || (c->have_underline))
4013 if (*style_pad_b < c->underline_extend)
4014 *style_pad_b = c->underline_extend;
4015 c->have_underline = 0;
4016 c->have_underline2 = 0;
4017 c->underline_extend = 0;
4029 fnode->is_new = EINA_FALSE;
4030 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
4032 _layout_text_append(c, c->fmt, n, start, -1, o->repch);
4034 /* Clear the bidi props because we don't need them anymore. */
4035 if (c->par->bidi_props)
4037 evas_bidi_paragraph_props_unref(c->par->bidi_props);
4038 c->par->bidi_props = NULL;
4041 c->par = (Evas_Object_Textblock_Paragraph *)
4042 EINA_INLIST_GET(c->par)->next;
4045 /* Delete the rest of the layout paragraphs */
4048 Evas_Object_Textblock_Paragraph *tmp_par =
4049 (Evas_Object_Textblock_Paragraph *)
4050 EINA_INLIST_GET(c->par)->next;
4052 c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4053 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
4054 EINA_INLIST_GET(c->par));
4055 _paragraph_free(obj, c->par);
4059 o->paragraphs = c->paragraphs;
4067 * Create the layout from the nodes.
4069 * @param obj the evas object - NOT NULL.
4070 * @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.
4071 * @param w the object's w, -1 means no wrapping (i.e infinite size)
4072 * @param h the object's h, -1 means inifinte size.
4073 * @param w_ret the object's calculated w.
4074 * @param h_ret the object's calculated h.
4077 _layout(const Evas_Object *obj, int w, int h, int *w_ret, int *h_ret)
4079 Evas_Object_Textblock *o;
4081 int style_pad_l = 0, style_pad_r = 0, style_pad_t = 0, style_pad_b = 0;
4084 o = (Evas_Object_Textblock *)(obj->object_data);
4086 c->obj = (Evas_Object *)obj;
4088 c->paragraphs = c->par = NULL;
4089 c->format_stack = NULL;
4094 c->wmax = c->hmax = 0;
4095 c->maxascent = c->maxdescent = 0;
4096 c->marginl = c->marginr = 0;
4097 c->have_underline = 0;
4098 c->have_underline2 = 0;
4099 c->underline_extend = 0;
4102 c->align_auto = EINA_TRUE;
4104 c->width_changed = (obj->cur.geometry.w != o->last_w);
4106 /* Start of logical layout creation */
4107 /* setup default base style */
4108 if ((c->o->style) && (c->o->style->default_tag))
4110 c->fmt = _layout_format_push(c, NULL, NULL);
4111 _format_fill(c->obj, c->fmt, c->o->style->default_tag);
4112 _format_finalize(c->obj, c->fmt);
4116 if (w_ret) *w_ret = 0;
4117 if (h_ret) *h_ret = 0;
4121 _layout_pre(c, &style_pad_l, &style_pad_r, &style_pad_t, &style_pad_b);
4122 c->paragraphs = o->paragraphs;
4124 /* If there are no paragraphs, create the minimum needed,
4125 * if the last paragraph has no lines/text, create that as well */
4128 _layout_paragraph_new(c, NULL, EINA_TRUE);
4129 o->paragraphs = c->paragraphs;
4131 c->par = (Evas_Object_Textblock_Paragraph *)
4132 EINA_INLIST_GET(c->paragraphs)->last;
4133 if (!c->par->logical_items)
4135 Evas_Object_Textblock_Text_Item *ti;
4136 ti = _layout_text_item_new(c, c->fmt);
4137 ti->parent.text_node = c->par->text_node;
4138 ti->parent.text_pos = 0;
4139 _layout_text_add_logical_item(c, ti, NULL);
4142 /* End of logical layout creation */
4144 /* Start of visual layout creation */
4146 Evas_Object_Textblock_Paragraph *last_vis_par = NULL;
4147 int par_index_step = o->num_paragraphs / TEXTBLOCK_PAR_INDEX_SIZE;
4148 int par_count = 1; /* Force it to take the first one */
4149 int par_index_pos = 0;
4151 if (par_index_step == 0) par_index_step = 1;
4153 /* Clear all of the index */
4154 memset(o->par_index, 0, sizeof(o->par_index));
4156 EINA_INLIST_FOREACH(c->paragraphs, c->par)
4158 _layout_update_par(c);
4160 /* Break if we should stop here. */
4163 last_vis_par = c->par;
4167 if ((par_index_pos < TEXTBLOCK_PAR_INDEX_SIZE) && (--par_count == 0))
4169 par_count = par_index_step;
4171 o->par_index[par_index_pos++] = c->par;
4175 /* Mark all the rest of the paragraphs as invisible */
4178 c->par = (Evas_Object_Textblock_Paragraph *)
4179 EINA_INLIST_GET(c->par)->next;
4182 c->par->visible = 0;
4183 c->par = (Evas_Object_Textblock_Paragraph *)
4184 EINA_INLIST_GET(c->par)->next;
4188 /* Get the last visible paragraph in the layout */
4189 if (!last_vis_par && c->paragraphs)
4190 last_vis_par = (Evas_Object_Textblock_Paragraph *)
4191 EINA_INLIST_GET(c->paragraphs)->last;
4194 c->hmax = last_vis_par->y + last_vis_par->h;
4197 /* Clean the rest of the format stack */
4198 while (c->format_stack)
4200 c->fmt = c->format_stack->data;
4201 c->format_stack = eina_list_remove_list(c->format_stack, c->format_stack);
4202 _format_unref_free(c->obj, c->fmt);
4205 if (w_ret) *w_ret = c->wmax;
4206 if (h_ret) *h_ret = c->hmax;
4208 /* Vertically align the textblock */
4209 if ((o->valign > 0.0) && (c->h > c->hmax))
4211 Evas_Coord adjustment = (c->h - c->hmax) * o->valign;
4212 Evas_Object_Textblock_Paragraph *par;
4213 EINA_INLIST_FOREACH(c->paragraphs, par)
4215 par->y += adjustment;
4219 if ((o->style_pad.l != style_pad_l) || (o->style_pad.r != style_pad_r) ||
4220 (o->style_pad.t != style_pad_t) || (o->style_pad.b != style_pad_b))
4222 o->style_pad.l = style_pad_l;
4223 o->style_pad.r = style_pad_r;
4224 o->style_pad.t = style_pad_t;
4225 o->style_pad.b = style_pad_b;
4226 _paragraphs_clear(obj, c->paragraphs);
4227 _layout(obj, w, h, w_ret, h_ret);
4233 * Relayout the object according to current object size.
4235 * @param obj the evas object - NOT NULL.
4238 _relayout(const Evas_Object *obj)
4240 Evas_Object_Textblock *o;
4242 o = (Evas_Object_Textblock *)(obj->object_data);
4243 _layout(obj, obj->cur.geometry.w, obj->cur.geometry.h,
4244 &o->formatted.w, &o->formatted.h);
4245 o->formatted.valid = 1;
4246 o->last_w = obj->cur.geometry.w;
4247 o->last_h = obj->cur.geometry.h;
4249 o->content_changed = 0;
4250 o->format_changed = EINA_FALSE;
4256 * Find the layout item and line that match the text node and position passed.
4258 * @param obj the evas object - NOT NULL.
4259 * @param n the text node - Not null.
4260 * @param pos the position to look for - valid.
4261 * @param[out] lnr the line found - not null.
4262 * @param[out] tir the item found - not null.
4263 * @see _find_layout_format_item_line_match()
4266 _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)
4268 Evas_Object_Textblock_Paragraph *found_par;
4269 Evas_Object_Textblock_Line *ln;
4270 Evas_Object_Textblock *o;
4272 o = (Evas_Object_Textblock *)(obj->object_data);
4273 if (!o->formatted.valid) _relayout(obj);
4278 _layout_paragraph_render(o, found_par);
4279 EINA_INLIST_FOREACH(found_par->lines, ln)
4281 Evas_Object_Textblock_Item *it;
4283 EINA_INLIST_FOREACH(ln->items, it)
4285 /* FIXME: p should be size_t, same goes for pos */
4286 int p = (int) it->text_pos;
4288 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
4290 Evas_Object_Textblock_Text_Item *ti =
4293 p += (int) ti->text_props.text_len;
4300 if (((pos >= (int) it->text_pos) && (pos < p)))
4318 * Return the line number 'line'.
4320 * @param obj the evas object - NOT NULL.
4321 * @param line the line to find
4322 * @return the line of line number or NULL if no line found.
4324 static Evas_Object_Textblock_Line *
4325 _find_layout_line_num(const Evas_Object *obj, int line)
4327 Evas_Object_Textblock_Paragraph *par;
4328 Evas_Object_Textblock_Line *ln;
4329 Evas_Object_Textblock *o;
4331 o = (Evas_Object_Textblock *)(obj->object_data);
4333 par = _layout_find_paragraph_by_line_no(o, line);
4336 _layout_paragraph_render(o, par);
4337 EINA_INLIST_FOREACH(par->lines, ln)
4339 if (par->line_no + ln->line_no == line) return ln;
4346 evas_object_textblock_add(Evas *e)
4350 MAGIC_CHECK(e, Evas, MAGIC_EVAS);
4353 obj = evas_object_new(e);
4354 evas_object_textblock_init(obj);
4355 evas_object_inject(obj, e);
4359 EAPI Evas_Textblock_Style *
4360 evas_textblock_style_new(void)
4362 Evas_Textblock_Style *ts;
4364 ts = calloc(1, sizeof(Evas_Textblock_Style));
4369 evas_textblock_style_free(Evas_Textblock_Style *ts)
4382 evas_textblock_style_set(Evas_Textblock_Style *ts, const char *text)
4388 /* If the style wasn't really changed, abort. */
4389 if ((!ts->style_text && !text) ||
4390 (ts->style_text && text && !strcmp(text, ts->style_text)))
4393 EINA_LIST_FOREACH(ts->objects, l, obj)
4395 Evas_Object_Textblock *o;
4397 o = (Evas_Object_Textblock *)(obj->object_data);
4398 _evas_textblock_invalidate_all(o);
4399 _evas_textblock_changed(o, obj);
4402 _style_replace(ts, text);
4406 // format MUST be KEY='VALUE'[KEY='VALUE']...
4408 const char *key_start, *key_stop, *val_start, *val_stop;
4410 key_start = key_stop = val_start = val_stop = NULL;
4421 if ((*p == '=') || (isspace(*p)))
4424 else if (!val_start)
4426 if (((*p) == '\'') && (*(p + 1)))
4431 if (((*p) == '\'') && (p > ts->style_text) && (p[-1] != '\\'))
4434 if ((key_start) && (key_stop) && (val_start) && (val_stop))
4436 char *tags, *replaces;
4437 Evas_Object_Style_Tag *tag;
4438 size_t tag_len = key_stop - key_start;
4439 size_t replace_len = val_stop - val_start;
4441 tags = malloc(tag_len + 1);
4444 memcpy(tags, key_start, tag_len);
4448 replaces = malloc(replace_len + 1);
4451 memcpy(replaces, val_start, replace_len);
4452 replaces[replace_len] = 0;
4454 if ((tags) && (replaces))
4456 if (!strcmp(tags, "DEFAULT"))
4458 ts->default_tag = replaces;
4463 tag = calloc(1, sizeof(Evas_Object_Style_Tag));
4467 tag->replace = replaces;
4468 tag->tag_len = tag_len;
4469 tag->replace_len = replace_len;
4470 ts->tags = (Evas_Object_Style_Tag *)eina_inlist_append(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
4481 if (tags) free(tags);
4482 if (replaces) free(replaces);
4484 key_start = key_stop = val_start = val_stop = NULL;
4492 evas_textblock_style_get(const Evas_Textblock_Style *ts)
4494 if (!ts) return NULL;
4495 return ts->style_text;
4498 /* textblock styles */
4500 evas_object_textblock_style_set(Evas_Object *obj, Evas_Textblock_Style *ts)
4503 if (ts == o->style) return;
4504 if ((ts) && (ts->delete_me)) return;
4507 Evas_Textblock_Style *old_ts;
4510 free(o->markup_text);
4511 o->markup_text = NULL;
4515 old_ts->objects = eina_list_remove(old_ts->objects, obj);
4516 if ((old_ts->delete_me) && (!old_ts->objects))
4517 evas_textblock_style_free(old_ts);
4521 ts->objects = eina_list_append(ts->objects, obj);
4525 _evas_textblock_invalidate_all(o);
4526 _evas_textblock_changed(o, obj);
4529 EAPI const Evas_Textblock_Style *
4530 evas_object_textblock_style_get(const Evas_Object *obj)
4532 TB_HEAD_RETURN(NULL);
4537 evas_object_textblock_replace_char_set(Evas_Object *obj, const char *ch)
4540 if (o->repch) eina_stringshare_del(o->repch);
4541 if (ch) o->repch = eina_stringshare_add(ch);
4542 else o->repch = NULL;
4543 _evas_textblock_invalidate_all(o);
4544 _evas_textblock_changed(o, obj);
4548 evas_object_textblock_legacy_newline_set(Evas_Object *obj, Eina_Bool mode)
4551 if (o->legacy_newline == mode)
4554 o->legacy_newline = mode;
4555 /* FIXME: Should recreate all the textnodes... For now, it's just
4556 * for new text inserted. */
4560 evas_object_textblock_legacy_newline_get(const Evas_Object *obj)
4562 TB_HEAD_RETURN(EINA_FALSE);
4563 return o->legacy_newline;
4567 evas_object_textblock_valign_set(Evas_Object *obj, double align)
4570 if (align < 0.0) align = 0.0;
4571 else if (align > 1.0) align = 1.0;
4572 if (o->valign == align) return;
4574 _evas_textblock_changed(o, obj);
4578 evas_object_textblock_valign_get(const Evas_Object *obj)
4580 TB_HEAD_RETURN(0.0);
4585 evas_object_textblock_bidi_delimiters_set(Evas_Object *obj, const char *delim)
4588 eina_stringshare_replace(&o->bidi_delimiters, delim);
4592 evas_object_textblock_bidi_delimiters_get(const Evas_Object *obj)
4594 TB_HEAD_RETURN(NULL);
4595 return o->bidi_delimiters;
4599 evas_object_textblock_replace_char_get(Evas_Object *obj)
4601 TB_HEAD_RETURN(NULL);
4607 * Advance p_buff to point after the end of the string. It's used with the
4608 * @ref escaped_strings[] variable.
4610 * @param p_buff the pointer to the current string.
4613 _escaped_advance_after_end_of_string(const char **p_buf)
4615 while (**p_buf != 0) (*p_buf)++;
4621 * Advance p_buff to point after the end of the string. It's used with the
4622 * @ref escaped_strings[] variable. Also chec if matches.
4625 * @param p_buff the pointer to the current string.
4628 _escaped_is_eq_and_advance(const char *s, const char *s_end,
4629 const char **p_m, const char *m_end)
4631 Eina_Bool reached_end;
4632 for (;((s < s_end) && (*p_m < m_end)); s++, (*p_m)++)
4636 _escaped_advance_after_end_of_string(p_m);
4641 reached_end = !**p_m;
4643 _escaped_advance_after_end_of_string(p_m);
4645 return ((s == s_end) && reached_end);
4651 * @param s the string to match
4653 static inline const char *
4654 _escaped_char_match(const char *s, int *adv)
4656 const char *map_itr, *map_end, *mc, *sc;
4658 map_itr = escape_strings;
4659 map_end = map_itr + sizeof(escape_strings);
4661 while (map_itr < map_end)
4667 _escaped_advance_after_end_of_string(&map_itr);
4668 if (map_itr >= map_end) break;
4673 while ((*mc) && (*sc))
4675 if ((unsigned char)*sc < (unsigned char)*mc) return NULL;
4676 if (*sc != *mc) match = 0;
4682 *adv = mc - map_itr;
4685 _escaped_advance_after_end_of_string(&map_itr);
4694 * @param s the string to match
4696 static inline const char *
4697 _escaped_char_get(const char *s, const char *s_end)
4699 /* Handle numeric escape codes. */
4702 static char utf8_escape[7]; /* Support up to 6 bytes utf8 */
4704 Eina_Unicode uchar[2] = { 0, 0 };
4708 s += 2; /* Skip "&#" */
4710 if (tolower(*s) == 'x')
4717 if (len >= sizeof(ustr) + 1)
4720 memcpy(ustr, s, len);
4722 uchar[0] = strtol(ustr, NULL, base);
4727 utf8_char = eina_unicode_unicode_to_utf8(uchar, NULL);
4728 strcpy(utf8_escape, utf8_char);
4735 const char *map_itr, *map_end;
4737 map_itr = escape_strings;
4738 map_end = map_itr + sizeof(escape_strings);
4740 while (map_itr < map_end)
4742 if (_escaped_is_eq_and_advance(s, s_end, &map_itr, map_end))
4744 if (map_itr < map_end)
4745 _escaped_advance_after_end_of_string(&map_itr);
4753 evas_textblock_escape_string_get(const char *escape)
4756 return _escaped_char_get(escape, escape + strlen(escape));
4760 evas_textblock_escape_string_range_get(const char *escape_start, const char *escape_end)
4762 return _escaped_char_get(escape_start, escape_end);
4766 evas_textblock_string_escape_get(const char *string, int *len_ret)
4769 return _escaped_char_match(string, len_ret);
4774 * Appends the escaped char beteewn s and s_end to the curosr
4777 * @param s the start of the string
4778 * @param s_end the end of the string.
4781 _append_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
4786 escape = _escaped_char_get(s, s_end);
4788 evas_textblock_cursor_text_append(cur, escape);
4793 * prepends the escaped char beteewn s and s_end to the curosr
4796 * @param s the start of the string
4797 * @param s_end the end of the string.
4800 _prepend_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
4805 escape = _escaped_char_get(s, s_end);
4807 evas_textblock_cursor_text_prepend(cur, escape);
4812 evas_object_textblock_text_markup_set(Evas_Object *obj, const char *text)
4815 if ((text != o->markup_text) && (o->markup_text))
4817 free(o->markup_text);
4818 o->markup_text = NULL;
4823 if (text != o->markup_text)
4825 if (text) o->markup_text = strdup(text);
4829 evas_textblock_cursor_paragraph_first(o->cursor);
4831 evas_object_textblock_text_markup_prepend(o->cursor, text);
4832 /* Point all the cursors to the starrt */
4835 Evas_Textblock_Cursor *data;
4837 evas_textblock_cursor_paragraph_first(o->cursor);
4838 EINA_LIST_FOREACH(o->cursors, l, data)
4839 evas_textblock_cursor_paragraph_first(data);
4844 evas_object_textblock_text_markup_prepend(Evas_Textblock_Cursor *cur, const char *text)
4846 Evas_Object *obj = cur->obj;
4851 char *tag_start, *tag_end, *esc_start, *esc_end;
4853 tag_start = tag_end = esc_start = esc_end = NULL;
4856 /* This loop goes through all of the mark up text until it finds format
4857 * tags, escape sequences or the terminating NULL. When it finds either
4858 * of those, it appends the text found up until that point to the textblock
4859 * proccesses whatever found. It repeats itself until the termainating
4860 * NULL is reached. */
4863 /* If we got to the end of string or just finished/started tag
4864 * or escape sequence handling. */
4866 (tag_end) || (esc_end) ||
4867 (tag_start) || (esc_start))
4871 /* If we reached to a tag ending, analyze the tag */
4873 size_t ttag_len = tag_end - tag_start;
4876 ttag = malloc(ttag_len + 1);
4879 memcpy(ttag, tag_start, ttag_len);
4881 evas_textblock_cursor_format_prepend(cur, ttag);
4884 tag_start = tag_end = NULL;
4888 _prepend_escaped_char(cur, esc_start, esc_end + 1);
4889 esc_start = esc_end = NULL;
4893 _prepend_text_run(cur, s, p);
4903 /* Append the text prior to this to the textblock and mark
4904 * the start of the tag */
4907 _prepend_text_run(cur, s, p);
4923 /* Append the text prior to this to the textblock and mark
4924 * the start of the escape sequence */
4927 _prepend_text_run(cur, s, p);
4939 /* Unicode object replcament char */
4940 else if (!strncmp("\xEF\xBF\xBC", p, 3))
4942 /*FIXME: currently just remove them, maybe do something
4943 * fancier in the future, atm it breaks if this char
4945 _prepend_text_run(cur, s, p);
4946 p += 2; /* it's also advanced later in this loop need +3
4948 s = p + 1; /* One after the end of the replacement char */
4953 _evas_textblock_changed(o, obj);
4959 * An helper function to markup get. Appends the format from fnode to the strbugf txt.
4961 * @param o the textblock object.
4962 * @param txt the strbuf to append to.
4963 * @param fnode the format node to process.
4966 _markup_get_format_append(Evas_Object_Textblock *o __UNUSED__, Eina_Strbuf *txt, Evas_Object_Textblock_Node_Format *fnode)
4968 eina_strbuf_append_char(txt, '<');
4973 // FIXME: need to escape
4974 s = fnode->orig_format;
4975 if (*s == '-') pop = 1;
4976 while ((*s == ' ') || (*s == '+') || (*s == '-')) s++;
4977 if (pop) eina_strbuf_append_char(txt, '/');
4978 eina_strbuf_append(txt, s);
4980 eina_strbuf_append_char(txt, '>');
4985 * An helper function to markup get. Appends the text in text.
4987 * @param txt the strbuf to append to.
4988 * @param text the text to process.
4991 _markup_get_text_append(Eina_Strbuf *txt, const Eina_Unicode *text)
4993 char *p = eina_unicode_unicode_to_utf8(text, NULL);
5000 escape = _escaped_char_match(p, &adv);
5004 eina_strbuf_append(txt, escape);
5008 eina_strbuf_append_char(txt, *p);
5015 evas_object_textblock_text_markup_get(const Evas_Object *obj)
5017 Evas_Object_Textblock_Node_Text *n;
5018 Eina_Strbuf *txt = NULL;
5020 TB_HEAD_RETURN(NULL);
5021 if (o->markup_text) return(o->markup_text);
5022 txt = eina_strbuf_new();
5023 EINA_INLIST_FOREACH(o->text_nodes, n)
5025 Evas_Object_Textblock_Node_Format *fnode;
5026 Eina_Unicode *text_base, *text;
5029 /* For each text node to thorugh all of it's format nodes
5030 * append text from the start to the offset of the next format
5031 * using the last format got. if needed it also creates format items
5032 * this is the core algorithm of the layout mechanism.
5033 * Skip the unicode replacement chars when there are because
5034 * we don't want to print them. */
5036 eina_unicode_strndup(eina_ustrbuf_string_get(n->unicode),
5037 eina_ustrbuf_length_get(n->unicode));
5038 fnode = n->format_node;
5040 while (fnode && (fnode->text_node == n))
5042 Eina_Unicode tmp_ch;
5043 off += fnode->offset;
5044 /* No need to skip on the first run */
5046 text[off] = 0; /* Null terminate the part of the string */
5047 _markup_get_text_append(txt, text);
5048 _markup_get_format_append(o, txt, fnode);
5049 text[off] = tmp_ch; /* Restore the char */
5060 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
5062 /* Add the rest, skip replacement */
5063 _markup_get_text_append(txt, text);
5068 o->markup_text = eina_strbuf_string_steal(txt);
5069 eina_strbuf_free(txt);
5070 return o->markup_text;
5077 * Merge the current node with the next, no need to remove PS, already
5080 * @param o the text block object.
5081 * @param to merge into to.
5084 _evas_textblock_nodes_merge(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *to)
5086 Evas_Object_Textblock_Node_Format *itr;
5087 Evas_Object_Textblock_Node_Format *pnode;
5088 Evas_Object_Textblock_Node_Text *from;
5089 const Eina_Unicode *text;
5093 from = _NODE_TEXT(EINA_INLIST_GET(to)->next);
5095 to_len = eina_ustrbuf_length_get(to->unicode);
5096 text = eina_ustrbuf_string_get(from->unicode);
5097 len = eina_ustrbuf_length_get(from->unicode);
5098 eina_ustrbuf_append_length(to->unicode, text, len);
5100 itr = from->format_node;
5101 if (itr && (itr->text_node == from))
5103 pnode = _NODE_FORMAT(EINA_INLIST_GET(itr)->prev);
5104 if (pnode && (pnode->text_node == to))
5106 itr->offset += to_len - _evas_textblock_node_format_pos_get(pnode);
5110 itr->offset += to_len;
5114 while (itr && (itr->text_node == from))
5116 itr->text_node = to;
5117 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
5119 if (!to->format_node || (to->format_node->text_node != to))
5121 to->format_node = from->format_node;
5124 /* When it comes to how we handle it, merging is like removing both nodes
5125 * and creating a new one, se we need to do the needed cleanups. */
5127 to->par->text_node = NULL;
5130 to->is_new = EINA_TRUE;
5132 _evas_textblock_cursors_set_node(o, from, to);
5133 _evas_textblock_node_text_remove(o, from);
5138 * Merge the current node with the next, no need to remove PS, already
5141 * @param cur the cursor that points to the current node
5144 _evas_textblock_cursor_nodes_merge(Evas_Textblock_Cursor *cur)
5146 Evas_Object_Textblock_Node_Text *nnode;
5147 Evas_Object_Textblock *o;
5151 len = eina_ustrbuf_length_get(cur->node->unicode);
5153 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5154 nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
5155 _evas_textblock_nodes_merge(o, cur->node);
5156 _evas_textblock_cursors_update_offset(cur, nnode, 0, len);
5157 _evas_textblock_cursors_set_node(o, nnode, cur->node);
5158 if (nnode == o->cursor->node)
5160 o->cursor->node = cur->node;
5161 o->cursor->pos += len;
5167 * Return the format at a specific position.
5169 * @param cur the cursor to the position.
5170 * @return the format node at the specific position or NULL if not found.
5172 static Evas_Object_Textblock_Node_Format *
5173 _evas_textblock_cursor_node_format_at_pos_get(const Evas_Textblock_Cursor *cur)
5175 Evas_Object_Textblock_Node_Format *node;
5176 Evas_Object_Textblock_Node_Format *itr;
5179 if (!cur->node) return NULL;
5181 node = cur->node->format_node;
5182 if (!node) return NULL;
5183 /* If there is no exclusive format node to this paragraph return the
5184 * previous's node */
5185 /* Find the main format node */
5186 EINA_INLIST_FOREACH(node, itr)
5188 if (itr->text_node != cur->node)
5192 if ((position + itr->offset) == cur->pos)
5196 position += itr->offset;
5203 * Return the last format node at the position of the format node n.
5205 * @param n a format node at the position.
5206 * @return the last format node at the position of n.
5208 static Evas_Object_Textblock_Node_Format *
5209 _evas_textblock_node_format_last_at_off(const Evas_Object_Textblock_Node_Format *n)
5211 const Evas_Object_Textblock_Node_Format *nnode;
5212 const Evas_Object_Textblock_Node_Text *tnode;
5213 if (!n) return NULL;
5215 tnode = n->text_node;
5219 nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
5221 while (nnode && (nnode->text_node == tnode) && (nnode->offset == 0));
5223 return (Evas_Object_Textblock_Node_Format *) n;
5228 * Returns the visible format at a specific location.
5230 * @param n a format at the specific position.
5231 * @return the format node at the specific position or NULL if not found.
5233 static Evas_Object_Textblock_Node_Format *
5234 _evas_textblock_node_visible_at_pos_get(const Evas_Object_Textblock_Node_Format *n)
5236 const Evas_Object_Textblock_Node_Format *nnode;
5237 if (!n) return NULL;
5238 /* The visible format is the last one, because it inserts a replacement
5239 * char that advances the next formats. */
5245 if (n->visible) return (Evas_Object_Textblock_Node_Format *) n;
5246 nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
5248 while (nnode && (nnode->offset == 0));
5255 * Return the last format that applies to a specific cursor or at the specific
5256 * position the cursor points to. This means either a cursor at or before the
5257 * position of the cursor in the text node is returned or the previous's text
5258 * node's format node.
5260 * @param cur the position to look at.
5261 * @return the format node found.
5263 static Evas_Object_Textblock_Node_Format *
5264 _evas_textblock_cursor_node_format_before_or_at_pos_get(const Evas_Textblock_Cursor *cur)
5266 Evas_Object_Textblock_Node_Format *node, *pitr = NULL;
5267 Evas_Object_Textblock_Node_Format *itr;
5268 size_t position = 0;
5270 if (!cur->node) return NULL;
5272 node = cur->node->format_node;
5273 if (!node) return NULL;
5274 /* If there is no exclusive format node to this paragraph return the
5275 * previous's node */
5276 if (node->text_node != cur->node)
5280 else if (node->offset > cur->pos)
5282 return _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5284 /* Find the main format node */
5285 pitr = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5286 EINA_INLIST_FOREACH(node, itr)
5288 if ((itr->text_node != cur->node) ||
5289 ((position + itr->offset) > cur->pos))
5293 else if ((position + itr->offset) == cur->pos)
5298 position += itr->offset;
5305 * Find the layout item and line that match the cursor.
5307 * @param cur the cursor we are currently at. - NOT NULL.
5308 * @param[out] lnr the line found - not null.
5309 * @param[out] itr the item found - not null.
5310 * @return EINA_TRUE if we matched the previous format, EINA_FALSE otherwise.
5313 _find_layout_item_match(const Evas_Textblock_Cursor *cur, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Item **itr)
5315 Evas_Textblock_Cursor cur2;
5316 Eina_Bool previous_format = EINA_FALSE;
5318 cur2.obj = cur->obj;
5319 evas_textblock_cursor_copy(cur, &cur2);
5325 if (_evas_textblock_cursor_is_at_the_end(cur) &&
5326 evas_textblock_cursor_format_is_visible_get(&cur2))
5328 _find_layout_item_line_match(cur2.obj, cur2.node, cur2.pos, lnr, itr);
5329 previous_format = EINA_TRUE;
5333 _find_layout_item_line_match(cur->obj, cur->node, cur->pos, lnr, itr);
5335 return previous_format;
5338 EAPI Evas_Textblock_Cursor *
5339 evas_object_textblock_cursor_get(const Evas_Object *obj)
5341 TB_HEAD_RETURN(NULL);
5345 EAPI Evas_Textblock_Cursor *
5346 evas_object_textblock_cursor_new(const Evas_Object *obj)
5348 Evas_Textblock_Cursor *cur;
5350 TB_HEAD_RETURN(NULL);
5351 cur = calloc(1, sizeof(Evas_Textblock_Cursor));
5352 cur->obj = (Evas_Object *) obj;
5353 cur->node = o->text_nodes;
5356 o->cursors = eina_list_append(o->cursors, cur);
5361 evas_textblock_cursor_free(Evas_Textblock_Cursor *cur)
5363 Evas_Object_Textblock *o;
5366 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5367 if (cur == o->cursor) return;
5368 o->cursors = eina_list_remove(o->cursors, cur);
5373 evas_textblock_cursor_is_format(const Evas_Textblock_Cursor *cur)
5375 if (!cur || !cur->node) return EINA_FALSE;
5376 if (evas_textblock_cursor_format_is_visible_get(cur)) return EINA_TRUE;
5377 return (_evas_textblock_cursor_node_format_at_pos_get(cur)) ?
5378 EINA_TRUE : EINA_FALSE;
5381 EAPI const Eina_List *
5382 evas_textblock_node_format_list_get(const Evas_Object *obj, const char *anchor)
5384 TB_HEAD_RETURN(NULL);
5385 if (!strcmp(anchor, "a"))
5386 return o->anchors_a;
5387 else if (!strcmp(anchor, "item"))
5388 return o->anchors_item;
5393 EAPI const Evas_Object_Textblock_Node_Format *
5394 evas_textblock_node_format_first_get(const Evas_Object *obj)
5396 TB_HEAD_RETURN(NULL);
5397 return o->format_nodes;
5400 EAPI const Evas_Object_Textblock_Node_Format *
5401 evas_textblock_node_format_last_get(const Evas_Object *obj)
5403 TB_HEAD_RETURN(NULL);
5404 if (o->format_nodes)
5406 return _NODE_FORMAT(EINA_INLIST_GET(o->format_nodes)->last);
5411 EAPI const Evas_Object_Textblock_Node_Format *
5412 evas_textblock_node_format_next_get(const Evas_Object_Textblock_Node_Format *n)
5414 return _NODE_FORMAT(EINA_INLIST_GET(n)->next);
5417 EAPI const Evas_Object_Textblock_Node_Format *
5418 evas_textblock_node_format_prev_get(const Evas_Object_Textblock_Node_Format *n)
5420 return _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
5424 evas_textblock_node_format_remove_pair(Evas_Object *obj,
5425 Evas_Object_Textblock_Node_Format *n)
5427 Evas_Object_Textblock_Node_Text *tnode1;
5428 Evas_Object_Textblock_Node_Format *fmt, *found_node = NULL;
5429 Eina_List *fstack = NULL;
5438 const char *fstr = fmt->orig_format;
5440 if (fstr && (*fstr == '+'))
5442 fstack = eina_list_prepend(fstack, fmt);
5444 else if (fstr && (*fstr == '-'))
5449 fstr_len = strlen(fstr);
5450 /* Generic popper, just pop */
5451 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
5453 fstack = eina_list_remove_list(fstack, fstack);
5460 /* Find the matching format and pop it, if the matching format
5461 * is out format, i.e the last one, pop and break. */
5465 Evas_Object_Textblock_Node_Format *fnode;
5466 EINA_LIST_FOREACH(fstack, i, fnode)
5468 if (_FORMAT_IS_CLOSER_OF(
5469 fnode->orig_format, fstr, fstr_len))
5471 /* Last one, this is our item! */
5472 if (!eina_list_next(i))
5477 fstack = eina_list_remove_list(fstack, i);
5484 fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
5486 while (fmt && fstack);
5490 fstack = eina_list_free(fstack);
5494 size_t ind = _evas_textblock_node_format_pos_get(n);
5495 const char *format = n->format;
5496 Evas_Textblock_Cursor cur;
5499 eina_ustrbuf_remove(n->text_node->unicode, ind, ind + 1);
5500 if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
5502 evas_textblock_cursor_at_format_set(&cur, n);
5503 _evas_textblock_cursor_nodes_merge(&cur);
5505 _evas_textblock_cursors_update_offset(&cur, n->text_node, ind, -1);
5507 tnode1 = n->text_node;
5508 _evas_textblock_node_format_remove(o, n, 0);
5509 if (found_node && (found_node != n))
5511 Evas_Object_Textblock_Node_Text *tnode2;
5512 tnode2 = found_node->text_node;
5513 /* found_node can never be visible! (it's the closing format) */
5514 _evas_textblock_node_format_remove(o, found_node, 0);
5516 /* FIXME: Should be unified in the layout, for example, added to a list
5517 * that checks this kind of removals. But until then, this is very fast
5519 /* Mark all the text nodes in between the removed formats as dirty. */
5522 tnode1->dirty = EINA_TRUE;
5523 if (tnode1 == tnode2)
5526 _NODE_TEXT(EINA_INLIST_GET(tnode1)->next);
5530 _evas_textblock_changed(o, obj);
5534 evas_textblock_cursor_paragraph_first(Evas_Textblock_Cursor *cur)
5536 Evas_Object_Textblock *o;
5538 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5539 cur->node = o->text_nodes;
5545 evas_textblock_cursor_paragraph_last(Evas_Textblock_Cursor *cur)
5547 Evas_Object_Textblock *o;
5548 Evas_Object_Textblock_Node_Text *node;
5551 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5552 node = o->text_nodes;
5555 node = _NODE_TEXT(EINA_INLIST_GET(node)->last);
5559 evas_textblock_cursor_paragraph_char_last(cur);
5570 evas_textblock_cursor_paragraph_next(Evas_Textblock_Cursor *cur)
5572 if (!cur) return EINA_FALSE;
5573 if (!cur->node) return EINA_FALSE;
5574 /* If there is a current text node, return the next text node (if exists)
5575 * otherwise, just return False. */
5578 Evas_Object_Textblock_Node_Text *nnode;
5579 nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
5592 evas_textblock_cursor_paragraph_prev(Evas_Textblock_Cursor *cur)
5594 Evas_Object_Textblock_Node_Text *node;
5595 if (!cur) return EINA_FALSE;
5596 if (!cur->node) return EINA_FALSE;
5597 /* If the current node is a text node, just get the prev if any,
5598 * if it's a format, get the current text node out of the format and return
5599 * the prev text node if any. */
5601 /* If there is a current text node, return the prev text node
5602 * (if exists) otherwise, just return False. */
5605 Evas_Object_Textblock_Node_Text *pnode;
5606 pnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->prev);
5610 evas_textblock_cursor_paragraph_char_last(cur);
5618 evas_textblock_cursor_set_at_format(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *n)
5620 evas_textblock_cursor_at_format_set(cur, n);
5624 evas_textblock_cursor_format_next(Evas_Textblock_Cursor *cur)
5626 Evas_Object_Textblock_Node_Format *node;
5628 if (!cur) return EINA_FALSE;
5629 if (!cur->node) return EINA_FALSE;
5630 /* If the current node is a format node, just get the next if any,
5631 * if it's a text, get the current format node out of the text and return
5632 * the next format node if any. */
5633 node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
5634 node = _evas_textblock_node_format_last_at_off(node);
5637 if (cur->node->format_node)
5639 cur->pos = _evas_textblock_node_format_pos_get(node);
5643 /* If there is a current text node, return the next format node (if exists)
5644 * otherwise, just return False. */
5647 Evas_Object_Textblock_Node_Format *nnode;
5648 nnode = _NODE_FORMAT(EINA_INLIST_GET(node)->next);
5651 cur->node = nnode->text_node;
5652 cur->pos = _evas_textblock_node_format_pos_get(nnode);
5661 evas_textblock_cursor_format_prev(Evas_Textblock_Cursor *cur)
5663 const Evas_Object_Textblock_Node_Format *node;
5664 if (!cur) return EINA_FALSE;
5665 if (!cur->node) return EINA_FALSE;
5666 node = evas_textblock_cursor_format_get(cur);
5669 node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
5672 cur->node = node->text_node;
5673 cur->pos = _evas_textblock_node_format_pos_get(node);
5678 /* If there is a current text node, return the next text node (if exists)
5679 * otherwise, just return False. */
5682 Evas_Object_Textblock_Node_Format *pnode;
5683 pnode = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5686 cur->node = pnode->text_node;
5687 cur->pos = _evas_textblock_node_format_pos_get(pnode);
5696 evas_textblock_cursor_char_next(Evas_Textblock_Cursor *cur)
5699 const Eina_Unicode *text;
5701 if (!cur) return EINA_FALSE;
5702 if (!cur->node) return EINA_FALSE;
5705 text = eina_ustrbuf_string_get(cur->node->unicode);
5706 if (text[ind]) ind++;
5707 /* Only allow pointing a null if it's the last paragraph.
5708 * because we don't have a PS there. */
5716 if (!evas_textblock_cursor_paragraph_next(cur))
5718 /* If we already were at the end, that means we don't have
5719 * where to go next we should return FALSE */
5720 if (cur->pos == (size_t) ind)
5734 evas_textblock_cursor_char_prev(Evas_Textblock_Cursor *cur)
5736 if (!cur) return EINA_FALSE;
5737 if (!cur->node) return EINA_FALSE;
5744 return evas_textblock_cursor_paragraph_prev(cur);
5748 evas_textblock_cursor_paragraph_char_first(Evas_Textblock_Cursor *cur)
5756 evas_textblock_cursor_paragraph_char_last(Evas_Textblock_Cursor *cur)
5761 if (!cur->node) return;
5762 ind = eina_ustrbuf_length_get(cur->node->unicode);
5763 /* If it's not the last paragraph, go back one, because we want to point
5764 * to the PS, not the NULL */
5765 if (EINA_INLIST_GET(cur->node)->next)
5776 evas_textblock_cursor_line_char_first(Evas_Textblock_Cursor *cur)
5778 Evas_Object_Textblock *o;
5779 Evas_Object_Textblock_Line *ln = NULL;
5780 Evas_Object_Textblock_Item *it = NULL;
5783 if (!cur->node) return;
5784 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5785 if (!o->formatted.valid) _relayout(cur->obj);
5787 _find_layout_item_match(cur, &ln, &it);
5792 Evas_Object_Textblock_Item *i;
5794 EINA_INLIST_FOREACH(ln->items, i)
5796 if (it->text_pos > i->text_pos)
5804 cur->pos = it->text_pos;
5805 cur->node = it->text_node;
5810 evas_textblock_cursor_line_char_last(Evas_Textblock_Cursor *cur)
5812 Evas_Object_Textblock *o;
5813 Evas_Object_Textblock_Line *ln = NULL;
5814 Evas_Object_Textblock_Item *it = NULL;
5817 if (!cur->node) return;
5818 o = (Evas_Object_Textblock *)(cur->obj->object_data);
5819 if (!o->formatted.valid) _relayout(cur->obj);
5821 _find_layout_item_match(cur, &ln, &it);
5826 Evas_Object_Textblock_Item *i;
5828 EINA_INLIST_FOREACH(ln->items, i)
5830 if (it->text_pos < i->text_pos)
5840 cur->node = it->text_node;
5841 cur->pos = it->text_pos;
5842 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
5844 ind = _ITEM_TEXT(it)->text_props.text_len - 1;
5845 if (!IS_AT_END(_ITEM_TEXT(it), ind)) ind++;
5848 else if (!EINA_INLIST_GET(ln)->next && !EINA_INLIST_GET(ln->par)->next)
5857 * checks if a format (as a string) is visible/changes format and sets the
5858 * fnode properties accordingly.
5860 * @param fnode the format node
5861 * @param s the string.
5864 _evas_textblock_format_is_visible(Evas_Object_Textblock_Node_Format *fnode,
5868 Eina_Bool is_opener = EINA_TRUE;
5870 fnode->visible = fnode->format_change = EINA_FALSE;
5871 fnode->anchor = ANCHOR_NONE;
5874 if (s[0] == '+' || s[0] == '-')
5876 is_opener = (s[0] == '+');
5878 fnode->format_change = EINA_TRUE;
5881 while ((item = _format_parse(&s)))
5883 int itlen = s - item;
5884 /* We care about all of the formats even after a - except for
5885 * item which we don't care after a - because it's just a standard
5887 if ((!strncmp(item, "\n", itlen) || !strncmp(item, "\\n", itlen)) ||
5888 (!strncmp(item, "\t", itlen) || !strncmp(item, "\\t", itlen)) ||
5889 (!strncmp(item, "ps", itlen) && (itlen >= 2)) ||
5890 (!strncmp(item, "item", itlen) && (itlen >= 4) && is_opener))
5892 fnode->visible = EINA_TRUE;
5895 if (is_opener && !strncmp(item, "a", itlen))
5897 fnode->anchor = ANCHOR_A;
5899 else if (is_opener && !strncmp(item, "item", itlen) && (itlen >= 4))
5901 fnode->anchor = ANCHOR_ITEM;
5907 * Sets the cursor to the position of where the fmt points to.
5909 * @param cur the cursor to update.
5910 * @param fmt the format to set according to.
5913 static void __UNUSED__
5914 _evas_textblock_cursor_node_text_at_format(Evas_Textblock_Cursor *cur, Evas_Object_Textblock_Node_Format *fmt)
5916 Evas_Object_Textblock_Node_Text *text;
5917 Evas_Object_Textblock_Node_Format *base_format;
5918 Evas_Object_Textblock_Node_Format *itr;
5919 size_t position = 0;
5921 if (!cur || !fmt) return;
5922 /* Find the main format node */
5923 text = fmt->text_node;
5925 base_format = text->format_node;
5926 EINA_INLIST_FOREACH(base_format, itr)
5932 position += itr->offset;
5934 cur->pos = position;
5941 * Remove pairs of + and - formats and also remove formats without + or -
5942 * i.e formats that pair to themselves. Only removes invisible formats
5943 * that pair themselves, if you want to remove invisible formats that pair
5944 * themselves, please first change fmt->visible to EINA_FALSE.
5946 * @param o the textblock object.
5947 * @param fmt the current format.
5950 _evas_textblock_node_format_remove_matching(Evas_Object_Textblock *o,
5951 Evas_Object_Textblock_Node_Format *fmt)
5953 Evas_Object_Textblock_Node_Text *tnode;
5954 Eina_List *formats = NULL;
5959 tnode = fmt->text_node;
5963 Evas_Object_Textblock_Node_Format *nnode;
5964 const char *fstr = fmt->orig_format;
5966 nnode = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
5969 offset = nnode->offset;
5973 if (fstr && (*fstr == '+'))
5975 formats = eina_list_prepend(formats, fmt);
5977 else if (fstr && (*fstr == '-'))
5979 Evas_Object_Textblock_Node_Format *fnode;
5983 fstr_len = strlen(fstr);
5984 /* Generic popper, just pop */
5985 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
5987 fnode = eina_list_data_get(formats);
5988 formats = eina_list_remove_list(formats, formats);
5989 _evas_textblock_node_format_remove(o, fnode, 0);
5990 _evas_textblock_node_format_remove(o, fmt, 0);
5992 /* Find the matching format and pop it, if the matching format
5993 * is our format, i.e the last one, pop and break. */
5996 Eina_List *i, *next;
5997 EINA_LIST_FOREACH_SAFE(formats, i, next, fnode)
5999 if (_FORMAT_IS_CLOSER_OF(
6000 fnode->orig_format, fstr, fstr_len))
6002 fnode = eina_list_data_get(i);
6003 formats = eina_list_remove_list(formats, i);
6004 _evas_textblock_node_format_remove(o, fnode, 0);
6005 _evas_textblock_node_format_remove(o, fmt, 0);
6011 else if (!fmt->visible)
6013 _evas_textblock_node_format_remove(o, fmt, 0);
6017 while (fmt && (offset == 0) && (fmt->text_node == tnode));
6018 eina_list_free(formats);
6022 * Add the offset (may be negative) to the first node after fmt which is
6023 * pointing to the text node tnode or to o->format_nodes if fmt is null
6024 * and it points to tnode.
6026 * @param o the textblock object.
6027 * @param tnode the text node the format should point to.
6028 * @param fmt the current format.
6029 * @param offset the offest to add (may be negative).
6032 _evas_textblock_node_format_adjust_offset(Evas_Object_Textblock *o,
6033 Evas_Object_Textblock_Node_Text *tnode,
6034 Evas_Object_Textblock_Node_Format *fmt, int offset)
6038 fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
6042 fmt = o->format_nodes;
6044 if (fmt && (tnode == fmt->text_node))
6046 fmt->offset += offset;
6052 * Removes a format node updating the offset of the next format node and the
6053 * text nodes pointing to this node.
6055 * @param o the textblock object.
6056 * @param n the fromat node to remove
6059 _evas_textblock_node_format_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n, int visible_adjustment)
6061 /* Update the text nodes about the change */
6063 Evas_Object_Textblock_Node_Format *nnode;
6064 nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->next);
6065 /* If there's a next node that belongs to the same text node
6066 * and the curret node was the main one, advance the format node */
6067 if (nnode && (nnode->text_node == n->text_node))
6069 if (nnode->text_node->format_node == n)
6071 nnode->text_node->format_node = nnode;
6076 Evas_Object_Textblock_Node_Text *tnode;
6077 /* If there's no next one update the text nodes */
6078 nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
6079 tnode = n->text_node;
6080 /* Even if it's not the current text_node's main node
6081 * it can still be the next's. */
6082 if (tnode && (tnode->format_node != n))
6084 tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
6086 while (tnode && (tnode->format_node == n))
6088 tnode->format_node = nnode;
6089 tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
6093 _evas_textblock_node_format_adjust_offset(o, n->text_node, n,
6094 n->offset - visible_adjustment);
6096 o->format_nodes = _NODE_FORMAT(eina_inlist_remove(
6097 EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n)));
6098 _evas_textblock_node_format_free(o, n);
6103 * Sets all the offsets of the format nodes between start and end in the text
6104 * node n to 0 and sets visibility to EINA_FALSE.
6105 * If end == -1 end means the end of the string.
6106 * Assumes there is a prev node or the current node will be preserved.
6108 * @param n the text node the positinos refer to.
6109 * @param start the start of where to delete from.
6110 * @param end the end of the section to delete, if end == -1 it means the end of the string.
6111 * @returns #EINA_TRUE if removed a PS, false otherwise.
6114 _evas_textblock_node_text_adjust_offsets_to_start(Evas_Object_Textblock *o,
6115 Evas_Object_Textblock_Node_Text *n, size_t start, int end)
6117 Evas_Object_Textblock_Node_Format *last_node, *itr;
6118 Evas_Object_Textblock_Node_Text *new_node;
6122 int update_format_node;
6126 itr = n->format_node;
6127 if (!itr || (itr->text_node != n)) return EINA_FALSE;
6130 if ((end < 0) || ((size_t) end == eina_ustrbuf_length_get(n->unicode)))
6136 /* We don't want the last one */
6140 /* If we are not removing the text node, all should stay in this text
6141 * node, otherwise, everything should move to the previous node */
6142 if ((start == 0) && !use_end)
6144 new_node = _NODE_TEXT(EINA_INLIST_GET(n)->prev);
6155 /* Find the first node after start */
6156 while (itr && (itr->text_node == n))
6163 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6166 if (!itr || (itr->text_node != n))
6171 update_format_node = ((itr == n->format_node) && (new_node != n));
6172 delta = orig_end - pos;
6173 itr->offset -= pos - start;
6175 while (itr && (itr->text_node == n))
6178 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6182 pos += last_node->offset;
6185 /* start is negative when this gets relevant */
6186 if (use_end && (pos > (size_t) end))
6188 last_node->offset -= delta;
6192 delta = orig_end - pos;
6195 last_node->offset = 0;
6201 last_node->visible = EINA_FALSE;
6203 if (!itr || (itr && (itr->text_node != n)))
6205 /* Remove the PS, and return since it's the end of the node */
6206 if (_IS_PARAGRAPH_SEPARATOR(o, last_node->format))
6208 _evas_textblock_node_format_remove(o, last_node, 0);
6213 last_node->text_node = new_node;
6214 if (update_format_node)
6216 n->format_node = last_node;
6225 * Removes all the format nodes between start and end in the text node n.
6226 * This function updates the offset of the next format node and the
6227 * text nodes pointing to it. if end == -1 end means the end of the string.
6229 * @param o the textblock object.
6230 * @param n the text node the positinos refer to.
6231 * @param start the start of where to delete from.
6232 * @param end the end of the section to delete, if end == -1 it means the end of the string.
6235 _evas_textblock_node_text_remove_formats_between(Evas_Object_Textblock *o,
6236 Evas_Object_Textblock_Node_Text *n, int start, int end)
6238 Evas_Object_Textblock_Node_Format *itr;
6240 int offset = end - start;
6241 itr = n->format_node;
6244 start -= itr->offset;
6245 if (offset < 0) offset = 0;
6246 if (end < 0) use_end = 0;
6247 while (itr && (itr->text_node == n))
6249 Evas_Object_Textblock_Node_Format *nnode;
6252 /* start is negative when this gets relevant */
6253 if ((offset + start < 0) && use_end)
6257 nnode = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6260 tmp_offset = nnode->offset;
6264 /* Don't do visible adjustments because we are removing the visual
6265 * chars anyway and taking those into account */
6266 _evas_textblock_node_format_remove(o, itr, 0);
6268 start -= tmp_offset;
6275 * Returns the first format in the range between start and end in the textblock
6278 * @param o the textblock object.
6279 * @param n the text node the positinos refer to.
6280 * @param start the start of where to delete from.
6281 * @param end the end of the section to delete, if end == -1 it means the end of the string.
6283 static Evas_Object_Textblock_Node_Format *
6284 _evas_textblock_node_text_get_first_format_between(
6285 Evas_Object_Textblock_Node_Text *n, int start, int end)
6287 Evas_Object_Textblock_Node_Format *itr;
6289 itr = n->format_node;
6290 if (end < 0) use_end = 0;
6291 while (itr && (itr->text_node == n))
6293 start -= itr->offset;
6295 if ((end <= 0) && use_end)
6303 itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6309 * Removes a text node and the corresponding format nodes.
6311 * @param o the textblock objec.t
6312 * @param n the node to remove.
6315 _evas_textblock_node_text_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *n)
6317 _evas_textblock_node_text_adjust_offsets_to_start(o, n, 0, -1);
6319 o->text_nodes = _NODE_TEXT(eina_inlist_remove(
6320 EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n)));
6321 _evas_textblock_node_text_free(n);
6326 * Return the position where the formats starts at.
6328 * @param fmt the format to return the position of.
6329 * @return the position of the format in the text node it points to.
6332 _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt)
6334 Evas_Object_Textblock_Node_Text *text;
6335 Evas_Object_Textblock_Node_Format *base_format;
6336 Evas_Object_Textblock_Node_Format *itr;
6337 size_t position = 0;
6340 /* Find the main format node */
6341 text = fmt->text_node;
6342 base_format = text->format_node;
6343 EINA_INLIST_FOREACH(base_format, itr)
6349 position += itr->offset;
6351 return position + fmt->offset;
6355 evas_textblock_cursor_pos_get(const Evas_Textblock_Cursor *cur)
6357 Evas_Object_Textblock *o;
6358 Evas_Object_Textblock_Node_Text *n;
6361 if (!cur) return -1;
6362 if (!cur->node) return 0;
6363 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6365 while (n != cur->node)
6367 npos += eina_ustrbuf_length_get(n->unicode);
6368 n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
6370 return npos + cur->pos;
6374 evas_textblock_cursor_pos_set(Evas_Textblock_Cursor *cur, int _pos)
6376 Evas_Object_Textblock *o;
6377 Evas_Object_Textblock_Node_Text *n;
6381 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6389 pos = (size_t) _pos;
6393 while (n && (pos >= eina_ustrbuf_length_get(n->unicode)))
6395 pos -= eina_ustrbuf_length_get(n->unicode);
6396 n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
6404 else if (o->text_nodes)
6406 /* In case we went pass the last node, we need to put the cursor
6407 * at the absolute end. */
6408 Evas_Object_Textblock_Node_Text *last_n;
6410 last_n = _NODE_TEXT(EINA_INLIST_GET(o->text_nodes)->last);
6411 pos = eina_ustrbuf_length_get(last_n->unicode);
6420 evas_textblock_cursor_line_set(Evas_Textblock_Cursor *cur, int line)
6422 Evas_Object_Textblock *o;
6423 Evas_Object_Textblock_Line *ln;
6424 Evas_Object_Textblock_Item *it;
6426 if (!cur) return EINA_FALSE;
6427 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6428 if (!o->formatted.valid) _relayout(cur->obj);
6430 ln = _find_layout_line_num(cur->obj, line);
6431 if (!ln) return EINA_FALSE;
6432 it = (Evas_Object_Textblock_Item *)ln->items;
6435 cur->pos = it->text_pos;
6436 cur->node = it->text_node;
6442 cur->node = o->text_nodes;
6448 evas_textblock_cursor_compare(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
6450 Eina_Inlist *l1, *l2;
6452 if (!cur1) return 0;
6453 if (!cur2) return 0;
6454 if (cur1->obj != cur2->obj) return 0;
6455 if ((!cur1->node) || (!cur2->node)) return 0;
6456 if (cur1->node == cur2->node)
6458 if (cur1->pos < cur2->pos) return -1; /* cur1 < cur2 */
6459 else if (cur1->pos > cur2->pos) return 1; /* cur2 < cur1 */
6462 for (l1 = EINA_INLIST_GET(cur1->node),
6463 l2 = EINA_INLIST_GET(cur1->node); (l1) || (l2);)
6465 if (l1 == EINA_INLIST_GET(cur2->node)) return 1; /* cur2 < cur 1 */
6466 else if (l2 == EINA_INLIST_GET(cur2->node)) return -1; /* cur1 < cur 2 */
6467 else if (!l1) return -1; /* cur1 < cur 2 */
6468 else if (!l2) return 1; /* cur2 < cur 1 */
6476 evas_textblock_cursor_copy(const Evas_Textblock_Cursor *cur, Evas_Textblock_Cursor *cur_dest)
6479 if (!cur_dest) return;
6480 if (cur->obj != cur_dest->obj) return;
6481 cur_dest->pos = cur->pos;
6482 cur_dest->node = cur->node;
6490 * Free a text node. Shouldn't be used usually, it's better to use
6491 * @ref _evas_textblock_node_text_remove for most cases .
6493 * @param n the text node to free
6494 * @see _evas_textblock_node_text_remove
6497 _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n)
6500 eina_ustrbuf_free(n->unicode);
6504 n->par->text_node = NULL;
6510 * Create a new text node
6512 * @return the new text node.
6514 static Evas_Object_Textblock_Node_Text *
6515 _evas_textblock_node_text_new(void)
6517 Evas_Object_Textblock_Node_Text *n;
6519 n = calloc(1, sizeof(Evas_Object_Textblock_Node_Text));
6520 n->unicode = eina_ustrbuf_new();
6521 /* We want to layout each paragraph at least once. */
6522 n->dirty = EINA_TRUE;
6523 n->is_new = EINA_TRUE;
6530 * Break a paragraph. This does not add a PS but only splits the paragraph
6531 * where a ps was just added!
6533 * @param cur the cursor to break at.
6534 * @param fnode the format node of the PS just added.
6535 * @return Returns no value.
6538 _evas_textblock_cursor_break_paragraph(Evas_Textblock_Cursor *cur,
6539 Evas_Object_Textblock_Node_Format *fnode)
6541 Evas_Object_Textblock *o;
6542 Evas_Object_Textblock_Node_Text *n;
6545 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6547 n = _evas_textblock_node_text_new();
6548 o->text_nodes = _NODE_TEXT(eina_inlist_append_relative(
6549 EINA_INLIST_GET(o->text_nodes),
6551 EINA_INLIST_GET(cur->node)));
6552 /* Handle text and format changes. */
6555 Evas_Object_Textblock_Node_Format *nnode;
6557 const Eina_Unicode *text;
6559 /* If there was a format node in the delete range,
6560 * make it our format and update the text_node fields,
6561 * otherwise, use the paragraph separator
6562 * of the previous paragraph. */
6563 nnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
6564 if (nnode && (nnode->text_node == cur->node))
6566 n->format_node = nnode;
6567 nnode->offset--; /* We don't have to take the replacement char
6568 into account anymore */
6569 while (nnode && (nnode->text_node == cur->node))
6571 nnode->text_node = n;
6572 nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
6577 n->format_node = fnode;
6580 /* cur->pos now points to the PS, move after. */
6581 start = cur->pos + 1;
6582 len = eina_ustrbuf_length_get(cur->node->unicode) - start;
6585 text = eina_ustrbuf_string_get(cur->node->unicode);
6586 eina_ustrbuf_append_length(n->unicode, text + start, len);
6587 eina_ustrbuf_remove(cur->node->unicode, start, start + len);
6588 cur->node->dirty = EINA_TRUE;
6593 fnode = o->format_nodes;
6596 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->last);
6598 n->format_node = fnode;
6604 * Set the node and offset of all the curs after cur.
6606 * @param cur the cursor.
6607 * @param n the current textblock node.
6608 * @param new_node the new node to set.
6611 _evas_textblock_cursors_set_node(Evas_Object_Textblock *o,
6612 const Evas_Object_Textblock_Node_Text *n,
6613 Evas_Object_Textblock_Node_Text *new_node)
6616 Evas_Textblock_Cursor *data;
6618 if (n == o->cursor->node)
6621 o->cursor->node = new_node;
6623 EINA_LIST_FOREACH(o->cursors, l, data)
6625 if (n == data->node)
6628 data->node = new_node;
6635 * Update the offset of all the cursors after cur.
6637 * @param cur the cursor.
6638 * @param n the current textblock node.
6639 * @param start the starting pos.
6640 * @param offset how much to adjust (can be negative).
6643 _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor *cur,
6644 const Evas_Object_Textblock_Node_Text *n,
6645 size_t start, int offset)
6648 Evas_Textblock_Cursor *data;
6649 Evas_Object_Textblock *o;
6650 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6652 if (cur != o->cursor)
6654 if ((n == o->cursor->node) &&
6655 (o->cursor->pos > start))
6657 if ((offset < 0) && (o->cursor->pos <= (size_t) (-1 * offset)))
6663 o->cursor->pos += offset;
6667 EINA_LIST_FOREACH(o->cursors, l, data)
6671 if ((n == data->node) &&
6672 (data->pos > start))
6674 if ((offset < 0) && (data->pos <= (size_t) (-1 * offset)))
6680 data->pos += offset;
6683 else if (!data->node)
6685 data->node = o->text_nodes;
6694 * Mark that the textblock has changed.
6696 * @param o the textblock object.
6697 * @param obj the evas object.
6700 _evas_textblock_changed(Evas_Object_Textblock *o, Evas_Object *obj)
6702 o->formatted.valid = 0;
6703 o->native.valid = 0;
6704 o->content_changed = 1;
6707 free(o->markup_text);
6708 o->markup_text = NULL;
6711 evas_object_change(obj);
6715 _evas_textblock_invalidate_all(Evas_Object_Textblock *o)
6717 Evas_Object_Textblock_Node_Text *n;
6719 EINA_INLIST_FOREACH(o->text_nodes, n)
6721 n->dirty = EINA_TRUE;
6726 evas_textblock_cursor_text_append(Evas_Textblock_Cursor *cur, const char *_text)
6728 Evas_Object_Textblock *o;
6729 Evas_Object_Textblock_Node_Text *n;
6730 Evas_Object_Textblock_Node_Format *fnode = NULL;
6735 text = eina_unicode_utf8_to_unicode(_text, &len);
6736 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6741 Evas_Object_Textblock_Node_Format *nnode;
6742 fnode = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
6743 fnode = _evas_textblock_node_format_last_at_off(fnode);
6744 /* find the node after the current in the same paragraph
6745 * either we find one and then take the next, or we try to get
6746 * the first for the paragraph which must be after our position */
6749 if (!evas_textblock_cursor_format_is_visible_get(cur))
6751 nnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
6752 if (nnode && (nnode->text_node == n))
6764 fnode = n->format_node;
6767 else if (o->text_nodes)
6769 cur->node = o->text_nodes;
6774 n = _evas_textblock_node_text_new();
6775 o->text_nodes = _NODE_TEXT(eina_inlist_append(
6776 EINA_INLIST_GET(o->text_nodes),
6777 EINA_INLIST_GET(n)));
6781 eina_ustrbuf_insert_length(n->unicode, text, len, cur->pos);
6782 /* Advance the formats */
6783 if (fnode && (fnode->text_node == cur->node))
6784 fnode->offset += len;
6786 /* Update all the cursors after our position. */
6787 _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, len);
6789 _evas_textblock_changed(o, cur->obj);
6790 n->dirty = EINA_TRUE;
6793 if (!o->cursor->node)
6794 o->cursor->node = o->text_nodes;
6799 evas_textblock_cursor_text_prepend(Evas_Textblock_Cursor *cur, const char *_text)
6802 /*append is essentially prepend without advancing */
6803 len = evas_textblock_cursor_text_append(cur, _text);
6804 cur->pos += len; /*Advance */
6810 * Free a format node
6812 * @param o the textblock object
6813 * @param n the format node to free
6816 _evas_textblock_node_format_free(Evas_Object_Textblock *o,
6817 Evas_Object_Textblock_Node_Format *n)
6820 eina_stringshare_del(n->format);
6821 eina_stringshare_del(n->orig_format);
6822 if (n->anchor == ANCHOR_ITEM)
6823 o->anchors_item = eina_list_remove(o->anchors_item, n);
6824 else if (n->anchor == ANCHOR_A)
6825 o->anchors_a = eina_list_remove(o->anchors_a, n);
6831 * Create a new format node.
6833 * @param format the text to create the format node from.
6834 * @param o the textblock object.
6835 * @return Returns the new format node
6837 static Evas_Object_Textblock_Node_Format *
6838 _evas_textblock_node_format_new(Evas_Object_Textblock *o, const char *_format)
6840 Evas_Object_Textblock_Node_Format *n;
6841 const char *format = _format;
6843 n = calloc(1, sizeof(Evas_Object_Textblock_Node_Format));
6844 /* Create orig_format and format */
6845 if (format[0] == '<')
6851 format++; /* Advance after '<' */
6852 format_len = strlen(format);
6853 if (format[format_len - 1] == '>')
6854 format_len--; /* We don't care about '>' */
6856 match = _style_match_tag(o->style, format, format_len, &replace_len);
6859 if ((match[0] == '+') || (match[0] == '-'))
6862 norm_format = malloc(format_len + 2 + 1);
6863 memcpy(norm_format, match, 2);
6864 memcpy(norm_format + 2, format, format_len);
6865 norm_format[format_len + 2] = '\0';
6867 eina_stringshare_add_length(norm_format, format_len + 2);
6873 eina_stringshare_add_length(format, format_len);
6875 n->format = eina_stringshare_add(match);
6881 norm_format = malloc(format_len + 2 + 1);
6884 if (format[0] == '/')
6886 memcpy(norm_format, "- ", 2);
6887 memcpy(norm_format + 2, format + 1, format_len - 1);
6888 norm_format[format_len + 2 - 1] = '\0';
6892 memcpy(norm_format, "+ ", 2);
6893 memcpy(norm_format + 2, format, format_len);
6894 norm_format[format_len + 2] = '\0';
6896 n->orig_format = eina_stringshare_add(norm_format);
6899 n->format = eina_stringshare_ref(n->orig_format);
6902 /* Just use as is, it's a special format. */
6905 n->orig_format = eina_stringshare_add(format);
6906 n->format = eina_stringshare_ref(n->orig_format);
6911 _evas_textblock_format_is_visible(n, format);
6912 if (n->anchor == ANCHOR_A)
6914 o->anchors_a = eina_list_append(o->anchors_a, n);
6916 else if (n->anchor == ANCHOR_ITEM)
6918 o->anchors_item = eina_list_append(o->anchors_item, n);
6920 n->is_new = EINA_TRUE;
6926 _evas_textblock_cursor_is_at_the_end(const Evas_Textblock_Cursor *cur)
6928 const Eina_Unicode *text;
6930 if (!cur) return EINA_FALSE;
6931 if (!cur->node) return EINA_FALSE;
6932 text = eina_ustrbuf_string_get(cur->node->unicode);
6933 return ((text[cur->pos] == 0) && (!EINA_INLIST_GET(cur->node)->next)) ?
6934 EINA_TRUE : EINA_FALSE;
6938 evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, const char *format)
6940 Evas_Object_Textblock *o;
6941 Evas_Object_Textblock_Node_Format *n;
6942 Eina_Bool is_visible;
6944 if (!cur) return EINA_FALSE;
6945 if ((!format) || (format[0] == 0)) return EINA_FALSE;
6946 o = (Evas_Object_Textblock *)(cur->obj->object_data);
6947 /* We should always have at least one text node */
6950 evas_textblock_cursor_text_prepend(cur, "");
6953 n = _evas_textblock_node_format_new(o, format);
6954 is_visible = n->visible;
6958 o->format_nodes = _NODE_FORMAT(eina_inlist_append(
6959 EINA_INLIST_GET(o->format_nodes),
6960 EINA_INLIST_GET(n)));
6962 n->text_node = (EINA_INLIST_GET(n)->prev) ?
6963 _NODE_FORMAT(EINA_INLIST_GET(n)->prev)->text_node :
6965 cur->node = n->text_node;
6969 Evas_Object_Textblock_Node_Format *fmt;
6970 fmt = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
6971 n->text_node = cur->node;
6974 o->format_nodes = _NODE_FORMAT(eina_inlist_prepend(
6975 EINA_INLIST_GET(o->format_nodes),
6976 EINA_INLIST_GET(n)));
6977 n->offset = cur->pos;
6981 if (evas_textblock_cursor_format_is_visible_get(cur))
6983 o->format_nodes = _NODE_FORMAT(eina_inlist_prepend_relative(
6984 EINA_INLIST_GET(o->format_nodes),
6986 EINA_INLIST_GET(fmt)
6988 n->offset = fmt->offset;
6989 if (fmt->text_node->format_node == fmt)
6991 fmt->text_node->format_node = n;
6996 fmt = _evas_textblock_node_format_last_at_off(fmt);
6997 o->format_nodes = _NODE_FORMAT(eina_inlist_append_relative(
6998 EINA_INLIST_GET(o->format_nodes),
7000 EINA_INLIST_GET(fmt)
7002 if (fmt->text_node != cur->node)
7004 n->offset = cur->pos;
7008 n->offset = cur->pos -
7009 _evas_textblock_node_format_pos_get(fmt);
7013 /* Adjust differently if we insert a format char */
7016 _evas_textblock_node_format_adjust_offset(o, cur->node, n,
7021 _evas_textblock_node_format_adjust_offset(o, cur->node, n,
7025 if (!fmt || (fmt->text_node != cur->node))
7027 cur->node->format_node = n;
7030 if (is_visible && cur->node)
7032 Eina_Unicode insert_char;
7033 /* Insert a visual representation according to the type of the
7035 if (_IS_PARAGRAPH_SEPARATOR(o, format))
7036 insert_char = _PARAGRAPH_SEPARATOR;
7037 else if (_IS_LINE_SEPARATOR(format))
7039 else if (_IS_TAB(format))
7042 insert_char = EVAS_TEXTBLOCK_REPLACEMENT_CHAR;
7044 eina_ustrbuf_insert_char(cur->node->unicode, insert_char, cur->pos);
7046 /* Advance all the cursors after our cursor */
7047 _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, 1);
7048 if (_IS_PARAGRAPH_SEPARATOR(o, format))
7050 _evas_textblock_cursor_break_paragraph(cur, n);
7054 /* Handle visible format nodes here */
7055 cur->node->dirty = EINA_TRUE;
7056 n->is_new = EINA_FALSE;
7061 o->format_changed = EINA_TRUE;
7064 _evas_textblock_changed(o, cur->obj);
7066 if (!o->cursor->node)
7067 o->cursor->node = o->text_nodes;
7072 evas_textblock_cursor_format_prepend(Evas_Textblock_Cursor *cur, const char *format)
7074 Eina_Bool is_visible;
7075 /* append is essentially prepend without advancing */
7076 is_visible = evas_textblock_cursor_format_append(cur, format);
7079 /* Advance after the replacement char */
7080 evas_textblock_cursor_char_next(cur);
7088 evas_textblock_cursor_char_delete(Evas_Textblock_Cursor *cur)
7090 Evas_Object_Textblock *o;
7091 Evas_Object_Textblock_Node_Text *n, *n2;
7092 const Eina_Unicode *text;
7095 if (!cur || !cur->node) return;
7096 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7099 text = eina_ustrbuf_string_get(n->unicode);
7106 if (chr == 0) return;
7108 eina_ustrbuf_remove(n->unicode, cur->pos, ind);
7109 /* Remove a format node if needed, and remove the char only if the
7110 * fmt node is not visible */
7112 Eina_Bool should_merge = EINA_FALSE;
7113 Evas_Object_Textblock_Node_Format *fmt, *fmt2;
7114 fmt = _evas_textblock_cursor_node_format_at_pos_get(cur);
7117 const char *format = NULL;
7118 Evas_Object_Textblock_Node_Format *last_fmt;
7119 /* If there's a PS it must be the last become it delimits paragraphs */
7120 last_fmt = _evas_textblock_node_format_last_at_off(fmt);
7121 format = last_fmt->format;
7122 if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
7124 /* If it was a paragraph separator, we should merge the
7125 * current with the next, there must be a next. */
7126 should_merge = EINA_TRUE;
7128 /* If a singnular, mark as invisible, so we'll delete it. */
7129 if (!format || ((*format != '+') && (*format != '-')))
7131 last_fmt->visible = EINA_FALSE;
7135 fmt2 = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
7136 fmt2 = _evas_textblock_node_format_last_at_off(fmt2);
7137 _evas_textblock_node_format_adjust_offset(o, cur->node, fmt2,
7142 _evas_textblock_cursor_nodes_merge(cur);
7145 _evas_textblock_node_format_remove_matching(o, fmt);
7148 if (cur->pos == eina_ustrbuf_length_get(n->unicode))
7150 n2 = _NODE_TEXT(EINA_INLIST_GET(n)->next);
7158 _evas_textblock_cursors_update_offset(cur, n, ppos, -(ind - ppos));
7159 _evas_textblock_changed(o, cur->obj);
7160 cur->node->dirty = EINA_TRUE;
7164 evas_textblock_cursor_range_delete(Evas_Textblock_Cursor *cur1, Evas_Textblock_Cursor *cur2)
7166 Evas_Object_Textblock_Node_Format *fnode = NULL;
7167 Evas_Object_Textblock *o;
7168 Evas_Object_Textblock_Node_Text *n1, *n2;
7169 Eina_Bool should_merge = EINA_FALSE, reset_cursor = EINA_FALSE;
7171 if (!cur1 || !cur1->node) return;
7172 if (!cur2 || !cur2->node) return;
7173 if (cur1->obj != cur2->obj) return;
7174 o = (Evas_Object_Textblock *)(cur1->obj->object_data);
7175 if (evas_textblock_cursor_compare(cur1, cur2) > 0)
7177 Evas_Textblock_Cursor *tc;
7185 if ((evas_textblock_cursor_compare(o->cursor, cur1) >= 0) &&
7186 (evas_textblock_cursor_compare(cur2, o->cursor) >= 0))
7188 reset_cursor = EINA_TRUE;
7194 if ((cur1->pos == 0) &&
7195 (cur2->pos == eina_ustrbuf_length_get(n1->unicode)))
7197 _evas_textblock_node_text_remove_formats_between(o, n1, 0, -1);
7201 should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o,
7202 n1, cur1->pos, cur2->pos);
7204 eina_ustrbuf_remove(n1->unicode, cur1->pos, cur2->pos);
7205 _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos, - (cur2->pos - cur1->pos));
7209 Evas_Object_Textblock_Node_Text *n;
7211 _evas_textblock_node_text_adjust_offsets_to_start(o, n1, cur1->pos, -1);
7212 n = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7213 /* Remove all the text nodes between */
7214 while (n && (n != n2))
7216 Evas_Object_Textblock_Node_Text *nnode;
7218 nnode = _NODE_TEXT(EINA_INLIST_GET(n)->next);
7219 _evas_textblock_cursors_set_node(o, n, n1);
7220 _evas_textblock_node_text_remove(o, n);
7223 should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o, n2,
7226 /* Remove the formats and the strings in the first and last nodes */
7227 len = eina_ustrbuf_length_get(n1->unicode);
7228 eina_ustrbuf_remove(n1->unicode, cur1->pos, len);
7229 eina_ustrbuf_remove(n2->unicode, 0, cur2->pos);
7230 /* Merge the nodes because we removed the PS */
7231 _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos,
7233 _evas_textblock_cursors_update_offset(cur2, cur2->node, 0, - cur2->pos);
7234 _evas_textblock_nodes_merge(o, n1);
7236 fnode = _evas_textblock_cursor_node_format_at_pos_get(cur1);
7240 /* We call this function instead of the cursor one because we already
7241 * updated the cursors */
7242 _evas_textblock_nodes_merge(o, n1);
7244 _evas_textblock_node_format_remove_matching(o, fnode);
7246 evas_textblock_cursor_copy(cur1, cur2);
7248 evas_textblock_cursor_copy(cur1, o->cursor);
7250 _evas_textblock_changed(o, cur1->obj);
7251 n1->dirty = n2->dirty = EINA_TRUE;
7256 evas_textblock_cursor_content_get(const Evas_Textblock_Cursor *cur)
7258 const Eina_Unicode *ustr;
7259 Eina_Unicode buf[2];
7261 if (!cur || !cur->node) return NULL;
7262 if (evas_textblock_cursor_format_is_visible_get(cur))
7268 fstr = evas_textblock_node_format_text_get(
7269 _evas_textblock_node_visible_at_pos_get(
7270 evas_textblock_cursor_format_get(cur)));
7275 if (*fstr == '-') pop = 1;
7276 while ((*fstr == ' ') || (*fstr == '+') || (*fstr == '-')) fstr++;
7283 ret = tmp = malloc(len + 3 + 1); /* </> and the null */
7284 memcpy(tmp, "</", 2);
7289 ret = tmp = malloc(len + 2 + 1); /* <> and the null */
7293 memcpy(tmp, fstr, len);
7294 memcpy(tmp + len, ">", 2); /* Including the null */
7300 ustr = eina_ustrbuf_string_get(cur->node->unicode);
7301 buf[0] = ustr[cur->pos];
7303 s = eina_unicode_unicode_to_utf8(buf, NULL);
7309 _evas_textblock_cursor_range_text_markup_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2)
7311 Evas_Object_Textblock *o;
7312 Evas_Object_Textblock_Node_Text *tnode;
7314 Evas_Textblock_Cursor *cur2;
7315 buf = eina_strbuf_new();
7317 if (!cur1 || !cur1->node) return NULL;
7318 if (!_cur2 || !_cur2->node) return NULL;
7319 if (cur1->obj != _cur2->obj) return NULL;
7320 o = (Evas_Object_Textblock *)(cur1->obj->object_data);
7321 if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
7323 const Evas_Textblock_Cursor *tc;
7329 /* Work on a local copy of the cur */
7330 cur2 = alloca(sizeof(Evas_Textblock_Cursor));
7331 cur2->obj = _cur2->obj;
7332 evas_textblock_cursor_copy(_cur2, cur2);
7334 /* Parse the text between the cursors. */
7335 for (tnode = cur1->node ; tnode ;
7336 tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next))
7338 Evas_Object_Textblock_Node_Format *fnode;
7339 Eina_Unicode *text_base, *text;
7343 eina_unicode_strndup(eina_ustrbuf_string_get(tnode->unicode),
7344 eina_ustrbuf_length_get(tnode->unicode));
7345 if (tnode == cur2->node)
7347 fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7348 cur1->pos, cur2->pos);
7350 else if (tnode == cur1->node)
7352 fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7357 fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7360 /* Init the offset so the first one will count starting from cur1->pos
7361 * and not the previous format node */
7362 if (tnode == cur1->node)
7366 off = _evas_textblock_node_format_pos_get(fnode) -
7367 cur1->pos - fnode->offset;
7375 while (fnode && (fnode->text_node == tnode))
7377 Eina_Unicode tmp_ch;
7378 off += fnode->offset;
7379 if ((tnode == cur2->node) &&
7380 ((size_t) (text - text_base + off) >= cur2->pos))
7384 /* No need to skip on the first run */
7386 text[off] = 0; /* Null terminate the part of the string */
7387 _markup_get_text_append(buf, text);
7388 _markup_get_format_append(o, buf, fnode);
7389 text[off] = tmp_ch; /* Restore the char */
7400 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7402 /* If we got to the last node, stop and add the rest outside */
7403 if (cur2->node == tnode)
7405 /* Add the rest, skip replacement */
7406 /* Don't go past the second cursor pos */
7407 text_base[cur2->pos] = '\0';
7408 _markup_get_text_append(buf, text);
7414 /* Add the rest, skip replacement */
7415 _markup_get_text_append(buf, text);
7419 /* return the string */
7422 ret = eina_strbuf_string_steal(buf);
7423 eina_strbuf_free(buf);
7429 _evas_textblock_cursor_range_text_plain_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2)
7432 Evas_Object_Textblock_Node_Text *n1, *n2;
7433 Evas_Textblock_Cursor *cur2;
7435 buf = eina_ustrbuf_new();
7437 if (!cur1 || !cur1->node) return NULL;
7438 if (!_cur2 || !_cur2->node) return NULL;
7439 if (cur1->obj != _cur2->obj) return NULL;
7440 if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
7442 const Evas_Textblock_Cursor *tc;
7450 /* Work on a local copy of the cur */
7451 cur2 = alloca(sizeof(Evas_Textblock_Cursor));
7452 cur2->obj = _cur2->obj;
7453 evas_textblock_cursor_copy(_cur2, cur2);
7458 const Eina_Unicode *tmp;
7459 tmp = eina_ustrbuf_string_get(n1->unicode);
7460 eina_ustrbuf_append_length(buf, tmp + cur1->pos, cur2->pos - cur1->pos);
7464 const Eina_Unicode *tmp;
7465 tmp = eina_ustrbuf_string_get(n1->unicode);
7466 eina_ustrbuf_append(buf, tmp + cur1->pos);
7467 n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7470 tmp = eina_ustrbuf_string_get(n1->unicode);
7471 eina_ustrbuf_append_length(buf, tmp,
7472 eina_ustrbuf_length_get(n1->unicode));
7473 n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7475 tmp = eina_ustrbuf_string_get(n2->unicode);
7476 eina_ustrbuf_append_length(buf, tmp, cur2->pos);
7479 /* Free and return */
7482 ret = eina_unicode_unicode_to_utf8(eina_ustrbuf_string_get(buf), NULL);
7483 eina_ustrbuf_free(buf);
7489 evas_textblock_cursor_range_formats_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
7491 Evas_Object *obj = cur1->obj;
7492 Eina_List *ret = NULL;
7493 Evas_Object_Textblock_Node_Text *n1, *n2;
7494 Evas_Object_Textblock_Node_Format *first, *last;
7495 TB_HEAD_RETURN(NULL);
7496 if (!cur1 || !cur1->node) return NULL;
7497 if (!cur2 || !cur2->node) return NULL;
7498 if (cur1->obj != cur2->obj) return NULL;
7499 if (evas_textblock_cursor_compare(cur1, cur2) > 0)
7501 const Evas_Textblock_Cursor *tc;
7510 /* FIXME: Change first and last getting to format_before_or_at_pos_get */
7512 last = n2->format_node;
7514 /* If n2->format_node is NULL, we don't have formats in the tb/range. */
7517 /* If the found format is on our text node, we should go to the last
7518 * one, otherwise, the one we found is good enough. */
7519 if (last->text_node == n2)
7521 Evas_Object_Textblock_Node_Format *fnode = last;
7522 while (fnode && (fnode->text_node == n2))
7525 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7529 /* If the first format node is within the range (i.e points to n1) or if
7530 * we have other formats in the range, go through them */
7531 first = n1->format_node;
7532 if ((first->text_node == n1) || (first != last))
7534 Evas_Object_Textblock_Node_Format *fnode = first;
7535 /* Go to the first one in the range */
7536 if (first->text_node != n1)
7538 first = _NODE_FORMAT(EINA_INLIST_GET(first)->next);
7543 ret = eina_list_append(ret, fnode);
7546 fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7555 evas_textblock_cursor_range_text_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2, Evas_Textblock_Text_Type format)
7557 if (format == EVAS_TEXTBLOCK_TEXT_MARKUP)
7558 return _evas_textblock_cursor_range_text_markup_get(cur1, cur2);
7559 else if (format == EVAS_TEXTBLOCK_TEXT_PLAIN)
7560 return _evas_textblock_cursor_range_text_plain_get(cur1, cur2);
7562 return NULL; /* Not yet supported */
7566 evas_textblock_cursor_paragraph_text_get(const Evas_Textblock_Cursor *cur)
7568 Evas_Textblock_Cursor cur1, cur2;
7569 if (!cur) return NULL;
7570 if (!cur->node) return NULL;
7571 if (cur->node->utf8)
7573 free(cur->node->utf8);
7575 cur1.obj = cur2.obj = cur->obj;
7576 cur1.node = cur2.node = cur->node;
7577 evas_textblock_cursor_paragraph_char_first(&cur1);
7578 evas_textblock_cursor_paragraph_char_last(&cur2);
7580 cur->node->utf8 = evas_textblock_cursor_range_text_get(&cur1, &cur2,
7581 EVAS_TEXTBLOCK_TEXT_MARKUP);
7582 return cur->node->utf8;
7586 evas_textblock_cursor_paragraph_text_length_get(const Evas_Textblock_Cursor *cur)
7589 if (!cur) return -1;
7590 if (!cur->node) return -1;
7591 len = eina_ustrbuf_length_get(cur->node->unicode);
7593 if (EINA_INLIST_GET(cur->node)->next)
7594 return len - 1; /* Remove the paragraph separator */
7599 EAPI const Evas_Object_Textblock_Node_Format *
7600 evas_textblock_cursor_format_get(const Evas_Textblock_Cursor *cur)
7602 if (!cur) return NULL;
7603 if (!cur->node) return NULL;
7604 return _evas_textblock_cursor_node_format_at_pos_get(cur);
7608 evas_textblock_node_format_text_get(const Evas_Object_Textblock_Node_Format *fmt)
7610 if (!fmt) return NULL;
7611 return fmt->orig_format;
7615 evas_textblock_cursor_at_format_set(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *fmt)
7617 if (!fmt || !cur) return;
7618 cur->node = fmt->text_node;
7619 cur->pos = _evas_textblock_node_format_pos_get(fmt);
7623 evas_textblock_cursor_format_is_visible_get(const Evas_Textblock_Cursor *cur)
7625 const Eina_Unicode *text;
7627 if (!cur) return EINA_FALSE;
7628 if (!cur->node) return EINA_FALSE;
7629 text = eina_ustrbuf_string_get(cur->node->unicode);
7630 return EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(text[cur->pos]);
7634 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)
7637 const Evas_Textblock_Cursor *dir_cur;
7638 Evas_Textblock_Cursor cur2;
7639 Evas_Object_Textblock *o;
7640 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7641 if (!o->formatted.valid) _relayout(cur->obj);
7644 if (ctype == EVAS_TEXTBLOCK_CURSOR_UNDER)
7646 ret = evas_textblock_cursor_pen_geometry_get(cur, cx, cy, cw, ch);
7648 else if (ctype == EVAS_TEXTBLOCK_CURSOR_BEFORE)
7650 /* In the case of a "before cursor", we should get the coordinates
7651 * of just after the previous char (which in bidi text may not be
7652 * just before the current char). */
7653 Evas_Coord x, y, h, w;
7654 Evas_Object_Textblock_Node_Format *fmt;
7656 /* If it's at the end of the line, we want to get the position, not
7657 * the position of the previous */
7658 if ((cur->pos > 0) && !_evas_textblock_cursor_is_at_the_end(cur))
7660 Eina_Bool before_char = EINA_FALSE;
7661 cur2.obj = cur->obj;
7662 evas_textblock_cursor_copy(cur, &cur2);
7663 evas_textblock_cursor_char_prev(&cur2);
7665 fmt = _evas_textblock_cursor_node_format_at_pos_get(&cur2);
7667 if (!fmt || !_IS_LINE_SEPARATOR(fmt->format))
7670 before_char = EINA_FALSE;
7674 before_char = EINA_TRUE;
7676 ret = evas_textblock_cursor_pen_geometry_get(
7677 dir_cur, &x, &y, &w, &h);
7679 /* Adjust if the char is an rtl char */
7682 Eina_Bool is_rtl = EINA_FALSE;
7683 if (dir_cur->node->par->is_bidi)
7685 Evas_Object_Textblock_Line *ln;
7686 Evas_Object_Textblock_Item *it;
7687 _find_layout_item_match(dir_cur, &ln, &it);
7688 if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
7689 (_ITEM_TEXT(it)->text_props.bidi.dir ==
7690 EVAS_BIDI_DIRECTION_RTL))
7692 else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
7693 (_ITEM_FORMAT(it)->bidi_dir ==
7694 EVAS_BIDI_DIRECTION_RTL))
7698 if ((!before_char && is_rtl) ||
7699 (before_char && !is_rtl))
7701 /* Just don't advance the width */
7707 else if (cur->pos == 0)
7709 ret = evas_textblock_cursor_pen_geometry_get(
7710 dir_cur, &x, &y, &w, &h);
7712 Eina_Bool is_rtl = EINA_FALSE;
7713 if (dir_cur->node->par->is_bidi)
7715 Evas_Object_Textblock_Line *ln;
7716 Evas_Object_Textblock_Item *it;
7717 _find_layout_item_match(dir_cur, &ln, &it);
7718 if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
7719 (_ITEM_TEXT(it)->text_props.bidi.dir ==
7720 EVAS_BIDI_DIRECTION_RTL))
7722 else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
7723 (_ITEM_FORMAT(it)->bidi_dir ==
7724 EVAS_BIDI_DIRECTION_RTL))
7728 /* Adjust if the char is an rtl char */
7729 if ((ret >= 0) && (!is_rtl))
7731 /* Just don't advance the width */
7738 ret = evas_textblock_cursor_pen_geometry_get(
7739 dir_cur, &x, &y, &w, &h);
7743 if (cx) *cx = x + w;
7750 if (dir && dir_cur && dir_cur->node)
7753 Eina_Bool is_rtl = EINA_FALSE;
7754 if (dir_cur->node->par->is_bidi)
7756 Evas_Object_Textblock_Line *ln;
7757 Evas_Object_Textblock_Item *it;
7758 _find_layout_item_match(dir_cur, &ln, &it);
7759 if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
7760 (_ITEM_TEXT(it)->text_props.bidi.dir ==
7761 EVAS_BIDI_DIRECTION_RTL))
7763 else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
7764 (_ITEM_FORMAT(it)->bidi_dir ==
7765 EVAS_BIDI_DIRECTION_RTL))
7769 if (_evas_textblock_cursor_is_at_the_end(dir_cur) && (dir_cur->pos > 0))
7772 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
7774 else if (dir_cur->pos > 0)
7777 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
7782 *dir = EVAS_BIDI_DIRECTION_LTR;
7790 * Returns the geometry/pen position (depending on query_func) of the char
7793 * @param cur the position of the char.
7794 * @param query_func the query function to use.
7795 * @param cx the x of the char (or pen_x in the case of pen position).
7796 * @param cy the y of the char.
7797 * @param cw the w of the char (or advance in the case pen position).
7798 * @param ch the h of the char.
7799 * @return line number of the char on success, -1 on error.
7802 _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)
7804 Evas_Object_Textblock *o;
7805 Evas_Object_Textblock_Line *ln = NULL;
7806 Evas_Object_Textblock_Item *it = NULL;
7807 Evas_Object_Textblock_Text_Item *ti = NULL;
7808 Evas_Object_Textblock_Format_Item *fi = NULL;
7809 int x = 0, y = 0, w = 0, h = 0;
7811 Eina_Bool previous_format;
7813 if (!cur) return -1;
7814 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7815 if (!o->formatted.valid) _relayout(cur->obj);
7821 if (!o->paragraphs) return -1;
7822 ln = o->paragraphs->lines;
7824 if (cx) *cx = ln->x;
7825 if (cy) *cy = ln->par->y + ln->y;
7826 if (cw) *cw = ln->w;
7827 if (ch) *ch = ln->h;
7828 return ln->par->line_no + ln->line_no;
7834 previous_format = _find_layout_item_match(cur, &ln, &it);
7839 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
7841 ti = _ITEM_TEXT(it);
7845 fi = _ITEM_FORMAT(it);
7850 pos = cur->pos - ti->parent.text_pos;
7852 if (pos < 0) pos = 0;
7853 if (ti->parent.format->font.font)
7855 query_func(cur->ENDT,
7856 ti->parent.format->font.font,
7862 x += ln->x + _ITEM(ti)->x;
7868 y = ln->par->y + ln->y;
7873 if (previous_format)
7875 if (_IS_LINE_SEPARATOR(fi->item))
7878 y = ln->par->y + ln->y + ln->h;
7883 if (ln->par->direction == EVAS_BIDI_DIRECTION_RTL)
7892 y = ln->par->y + ln->y;
7899 x = ln->x + _ITEM(fi)->x;
7900 y = ln->par->y + ln->y;
7913 return ln->par->line_no + ln->line_no;
7917 evas_textblock_cursor_char_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
7919 return _evas_textblock_cursor_char_pen_geometry_common_get(
7920 cur->ENFN->font_char_coords_get, cur, cx, cy, cw, ch);
7924 evas_textblock_cursor_pen_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
7926 return _evas_textblock_cursor_char_pen_geometry_common_get(
7927 cur->ENFN->font_pen_coords_get, cur, cx, cy, cw, ch);
7931 evas_textblock_cursor_line_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
7933 Evas_Object_Textblock *o;
7934 Evas_Object_Textblock_Line *ln = NULL;
7935 Evas_Object_Textblock_Item *it = NULL;
7938 if (!cur) return -1;
7939 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7940 if (!o->formatted.valid) _relayout(cur->obj);
7943 ln = o->paragraphs->lines;
7947 _find_layout_item_match(cur, &ln, &it);
7951 y = ln->par->y + ln->y;
7958 return ln->par->line_no + ln->line_no;
7962 evas_textblock_cursor_visible_range_get(Evas_Textblock_Cursor *start, Evas_Textblock_Cursor *end)
7966 Evas_Object *obj = start->obj;
7967 TB_HEAD_RETURN(EINA_FALSE);
7968 e = evas_object_evas_get(obj);
7969 cy = 0 - obj->cur.geometry.y;
7971 evas_textblock_cursor_line_coord_set(start, cy);
7972 evas_textblock_cursor_line_coord_set(end, cy + ch);
7973 evas_textblock_cursor_line_char_last(end);
7979 evas_textblock_cursor_char_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord x, Evas_Coord y)
7981 Evas_Object_Textblock *o;
7982 Evas_Object_Textblock_Paragraph *found_par;
7983 Evas_Object_Textblock_Line *ln;
7984 Evas_Object_Textblock_Item *it = NULL;
7986 if (!cur) return EINA_FALSE;
7987 o = (Evas_Object_Textblock *)(cur->obj->object_data);
7988 if (!o->formatted.valid) _relayout(cur->obj);
7989 x += o->style_pad.l;
7990 y += o->style_pad.t;
7992 found_par = _layout_find_paragraph_by_y(o, y);
7995 _layout_paragraph_render(o, found_par);
7996 EINA_INLIST_FOREACH(found_par->lines, ln)
7998 if (ln->par->y + ln->y > y) break;
7999 if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
8001 /* If before or after the line, go to start/end according
8002 * to paragraph direction. */
8005 cur->pos = ln->items->text_pos;
8006 cur->node = found_par->text_node;
8007 if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
8009 evas_textblock_cursor_line_char_last(cur);
8013 evas_textblock_cursor_line_char_first(cur);
8017 else if (x >= ln->x + ln->w)
8019 cur->pos = ln->items->text_pos;
8020 cur->node = found_par->text_node;
8021 if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
8023 evas_textblock_cursor_line_char_first(cur);
8027 evas_textblock_cursor_line_char_last(cur);
8032 EINA_INLIST_FOREACH(ln->items, it)
8034 if (((it->x + ln->x) <= x) && (((it->x + ln->x) + it->adv) > x))
8036 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8040 Evas_Object_Textblock_Text_Item *ti;
8041 ti = _ITEM_TEXT(it);
8044 if (ti->parent.format->font.font)
8045 pos = cur->ENFN->font_char_at_coords_get(
8047 ti->parent.format->font.font,
8049 x - it->x - ln->x, 0,
8050 &cx, &cy, &cw, &ch);
8053 cur->pos = pos + it->text_pos;
8054 cur->node = it->text_node;
8059 Evas_Object_Textblock_Format_Item *fi;
8060 fi = _ITEM_FORMAT(it);
8061 cur->pos = fi->parent.text_pos;
8062 cur->node = found_par->text_node;
8070 else if (o->paragraphs && (y >= o->paragraphs->y + o->formatted.h))
8072 /* If we are after the last paragraph, use the last position in the
8074 evas_textblock_cursor_paragraph_last(cur);
8077 else if (o->paragraphs && (y < o->paragraphs->y))
8079 evas_textblock_cursor_paragraph_first(cur);
8087 evas_textblock_cursor_line_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord y)
8089 Evas_Object_Textblock *o;
8090 Evas_Object_Textblock_Paragraph *found_par;
8091 Evas_Object_Textblock_Line *ln;
8093 if (!cur) return -1;
8094 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8095 if (!o->formatted.valid) _relayout(cur->obj);
8096 y += o->style_pad.t;
8098 found_par = _layout_find_paragraph_by_y(o, y);
8102 _layout_paragraph_render(o, found_par);
8103 EINA_INLIST_FOREACH(found_par->lines, ln)
8105 if (ln->par->y + ln->y > y) break;
8106 if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
8108 evas_textblock_cursor_line_set(cur, ln->par->line_no +
8110 return ln->par->line_no + ln->line_no;
8114 else if (o->paragraphs && (y >= o->paragraphs->y + o->formatted.h))
8117 /* If we are after the last paragraph, use the last position in the
8119 evas_textblock_cursor_paragraph_last(cur);
8120 if (cur->node && cur->node->par)
8122 line_no = cur->node->par->line_no;
8123 if (cur->node->par->lines)
8125 line_no += ((Evas_Object_Textblock_Line *)
8126 EINA_INLIST_GET(cur->node->par->lines)->last)->line_no;
8131 else if (o->paragraphs && (y < o->paragraphs->y))
8134 evas_textblock_cursor_paragraph_first(cur);
8135 if (cur->node && cur->node->par)
8137 line_no = cur->node->par->line_no;
8146 * Updates x and w according to the text direction, position in text and
8147 * if it's a special case switch
8149 * @param ti the text item we are working on
8150 * @param x the current x (we get) and the x we return
8151 * @param w the current w (we get) and the w we return
8152 * @param start if this is the first item or not
8153 * @param switch_items toogles item switching (rtl cases)
8156 _evas_textblock_range_calc_x_w(const Evas_Object_Textblock_Item *it,
8157 Evas_Coord *x, Evas_Coord *w, Eina_Bool start, Eina_Bool switch_items)
8159 if ((start && !switch_items) || (!start && switch_items))
8162 if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8163 _ITEM_TEXT(it)->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8165 ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8166 _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
8180 if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8181 _ITEM_TEXT(it)->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8183 ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8184 _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
8201 * Returns the geometry of the range in line ln. Cur1 is the start cursor,
8202 * cur2 is the end cursor, NULL means from the start or to the end accordingly.
8203 * Assumes that ln is valid, and that at least one of cur1 and cur2 is not NULL.
8205 * @param ln the line to work on.
8206 * @param cur1 the start cursor
8207 * @param cur2 the end cursor
8208 * @return Returns the geometry of the range
8211 _evas_textblock_cursor_range_in_line_geometry_get(
8212 const Evas_Object_Textblock_Line *ln, const Evas_Textblock_Cursor *cur1,
8213 const Evas_Textblock_Cursor *cur2)
8215 Evas_Object_Textblock_Item *it;
8216 Evas_Object_Textblock_Item *it1, *it2;
8217 Eina_List *rects = NULL;
8218 Evas_Textblock_Rectangle *tr;
8220 Eina_Bool switch_items;
8221 const Evas_Textblock_Cursor *cur;
8223 cur = (cur1) ? cur1 : cur2;
8225 /* Find the first and last items */
8228 EINA_INLIST_FOREACH(ln->items, it)
8231 item_len = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
8232 _ITEM_TEXT(it)->text_props.text_len
8234 if ((!cur1 || (cur1->pos < it->text_pos + item_len)) &&
8235 (!cur2 || (cur2->pos >= it->text_pos)))
8240 start = item_len; /* start stores the first item_len */
8243 end = item_len; /* end stores the last item_len */
8247 /* If we couldn't find even one item, return */
8248 if (!it1) return NULL;
8250 /* If the first item is logically before or equal the second item
8251 * we have to set start and end differently than in the other case */
8252 if (it1->text_pos <= it2->text_pos)
8254 start = (cur1) ? (cur1->pos - it1->text_pos) : 0;
8255 end = (cur2) ? (cur2->pos - it2->text_pos) : end;
8256 switch_items = EINA_FALSE;
8260 start = (cur2) ? (cur2->pos - it1->text_pos) : start;
8261 end = (cur1) ? (cur1->pos - it2->text_pos) : 0;
8262 switch_items = EINA_TRUE;
8265 /* IMPORTANT: Don't use cur1/cur2 past this point (because they probably
8266 * don't make sense anymore. That's why there are start and end),
8267 * unless you know what you are doing */
8269 /* Special case when they share the same item and it's a text item */
8270 if ((it1 == it2) && (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT))
8272 Evas_Coord x1, w1, x2, w2;
8273 Evas_Coord x, w, y, h;
8274 Evas_Object_Textblock_Text_Item *ti;
8277 ti = _ITEM_TEXT(it1);
8278 if (ti->parent.format->font.font)
8280 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8281 ti->parent.format->font.font,
8290 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8291 ti->parent.format->font.font,
8300 /* Make x2 the one on the right */
8314 if (ti->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8327 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8328 rects = eina_list_append(rects, tr);
8329 tr->x = ln->x + it1->x + x;
8330 tr->y = ln->par->y + ln->y;
8335 else if (it1 != it2)
8337 /* Get the middle items */
8338 Evas_Coord min_x, max_x;
8340 it = _ITEM(EINA_INLIST_GET(it1)->next);
8341 min_x = max_x = it->x;
8343 if (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8346 Evas_Object_Textblock_Text_Item *ti;
8348 ti = _ITEM_TEXT(it1);
8350 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8351 ti->parent.format->font.font,
8357 /* BUG! Skip the first item */
8362 _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
8370 _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
8375 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8376 rects = eina_list_append(rects, tr);
8377 tr->x = ln->x + it1->x + x;
8378 tr->y = ln->par->y + ln->y;
8383 while (it && (it != it2))
8385 max_x = it->x + it->adv;
8386 it = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(it)->next;
8390 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8391 rects = eina_list_append(rects, tr);
8392 tr->x = ln->x + min_x;
8393 tr->y = ln->par->y + ln->y;
8395 tr->w = max_x - min_x;
8397 if (it2->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8400 Evas_Object_Textblock_Text_Item *ti;
8402 ti = _ITEM_TEXT(it2);
8404 ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8405 ti->parent.format->font.font,
8411 /* BUG! skip the last item */
8416 _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
8424 _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
8429 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8430 rects = eina_list_append(rects, tr);
8431 tr->x = ln->x + it2->x + x;
8432 tr->y = ln->par->y + ln->y;
8440 evas_textblock_cursor_range_geometry_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
8442 Evas_Object_Textblock *o;
8443 Evas_Object_Textblock_Line *ln1, *ln2;
8444 Evas_Object_Textblock_Item *it1, *it2;
8445 Eina_List *rects = NULL;
8446 Evas_Textblock_Rectangle *tr;
8448 if (!cur1 || !cur1->node) return NULL;
8449 if (!cur2 || !cur2->node) return NULL;
8450 if (cur1->obj != cur2->obj) return NULL;
8451 o = (Evas_Object_Textblock *)(cur1->obj->object_data);
8452 if (!o->formatted.valid) _relayout(cur1->obj);
8453 if (evas_textblock_cursor_compare(cur1, cur2) > 0)
8455 const Evas_Textblock_Cursor *tc;
8464 _find_layout_item_match(cur1, &ln1, &it1);
8465 if (!ln1 || !it1) return NULL;
8466 _find_layout_item_match(cur2, &ln2, &it2);
8467 if (!ln2 || !it2) return NULL;
8471 rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
8476 Evas_Object_Textblock_Line *plni, *lni;
8477 Eina_List *rects2 = NULL;
8478 /* Handle the first line */
8479 rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
8482 /* Handle the lines between the first and the last line */
8483 lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(ln1)->next;
8484 if (!lni && (ln1->par != ln2->par))
8486 lni = ((Evas_Object_Textblock_Paragraph *)
8487 EINA_INLIST_GET(ln1->par)->next)->lines;
8489 while (lni && (lni != ln2))
8491 tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8492 rects = eina_list_append(rects, tr);
8494 tr->y = lni->par->y + lni->y;
8498 lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(lni)->next;
8499 if (!lni && (plni->par != ln2->par))
8501 lni = ((Evas_Object_Textblock_Paragraph *)
8502 EINA_INLIST_GET(plni->par)->next)->lines;
8505 rects2 = _evas_textblock_cursor_range_in_line_geometry_get(ln2,
8507 rects = eina_list_merge(rects, rects2);
8513 evas_textblock_cursor_format_item_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8515 Evas_Object_Textblock *o;
8516 Evas_Object_Textblock_Line *ln = NULL;
8517 Evas_Object_Textblock_Format_Item *fi;
8518 Evas_Object_Textblock_Item *it = NULL;
8519 Evas_Coord x, y, w, h;
8521 if (!cur || !evas_textblock_cursor_format_is_visible_get(cur)) return EINA_FALSE;
8522 o = (Evas_Object_Textblock *)(cur->obj->object_data);
8523 if (!o->formatted.valid) _relayout(cur->obj);
8524 if (!evas_textblock_cursor_format_is_visible_get(cur)) return EINA_FALSE;
8525 _find_layout_item_line_match(cur->obj, cur->node, cur->pos, &ln, &it);
8526 fi = _ITEM_FORMAT(it);
8527 if ((!ln) || (!fi)) return EINA_FALSE;
8528 x = ln->x + fi->parent.x;
8529 y = ln->par->y + ln->y + ln->baseline + fi->y;
8540 evas_textblock_cursor_eol_get(const Evas_Textblock_Cursor *cur)
8542 Eina_Bool ret = EINA_FALSE;
8543 Evas_Textblock_Cursor cur2;
8544 if (!cur) return EINA_FALSE;
8546 cur2.obj = cur->obj;
8547 evas_textblock_cursor_copy(cur, &cur2);
8548 evas_textblock_cursor_line_char_last(&cur2);
8549 if (cur2.pos == cur->pos)
8556 /* general controls */
8558 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)
8560 Evas_Object_Textblock_Line *ln;
8563 ln = _find_layout_line_num(obj, line);
8564 if (!ln) return EINA_FALSE;
8565 if (cx) *cx = ln->x;
8566 if (cy) *cy = ln->par->y + ln->y;
8567 if (cw) *cw = ln->w;
8568 if (ch) *ch = ln->h;
8573 evas_object_textblock_clear(Evas_Object *obj)
8576 Evas_Textblock_Cursor *cur;
8581 _paragraphs_free(obj, o->paragraphs);
8582 o->paragraphs = NULL;
8586 o->cursor->node = NULL;
8588 EINA_LIST_FOREACH(o->cursors, l, cur)
8594 _evas_textblock_changed(o, obj);
8598 evas_object_textblock_size_formatted_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
8601 if (!o->formatted.valid) _relayout(obj);
8602 if (w) *w = o->formatted.w;
8603 if (h) *h = o->formatted.h;
8607 _size_native_calc_line_finalize(const Evas_Object *obj, Eina_List *items,
8608 Evas_Coord *ascent, Evas_Coord *descent, Evas_Coord *w)
8610 Evas_Object_Textblock_Item *it;
8613 it = eina_list_data_get(items);
8614 /* If there are no text items yet, calc ascent/descent
8615 * according to the current format. */
8616 if (it && (*ascent + *descent == 0))
8617 _layout_format_ascent_descent_adjust(obj, ascent, descent, it->format);
8620 /* Adjust all the item sizes according to the final line size,
8621 * and update the x positions of all the items of the line. */
8622 EINA_LIST_FOREACH(items, i, it)
8624 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
8626 Evas_Coord fw, fh, fy;
8628 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
8629 if (!fi->formatme) goto loop_advance;
8630 _layout_calculate_format_item_size(obj, fi, ascent,
8631 descent, &fy, &fw, &fh);
8641 _size_native_calc_paragraph_size(const Evas_Object *obj,
8642 const Evas_Object_Textblock *o,
8643 const Evas_Object_Textblock_Paragraph *par,
8644 Evas_Coord *_w, Evas_Coord *_h)
8647 Evas_Object_Textblock_Item *it;
8648 Eina_List *line_items = NULL;
8649 Evas_Coord w = 0, y = 0, wmax = 0, h = 0, ascent = 0, descent = 0;
8651 EINA_LIST_FOREACH(par->logical_items, i, it)
8653 line_items = eina_list_append(line_items, it);
8654 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
8656 Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
8657 if (fi->item && (_IS_LINE_SEPARATOR(fi->item) ||
8658 _IS_PARAGRAPH_SEPARATOR(o, fi->item)))
8660 _size_native_calc_line_finalize(obj, line_items, &ascent,
8663 if (ascent + descent > h)
8664 h = ascent + descent;
8670 ascent = descent = 0;
8671 line_items = eina_list_free(line_items);
8675 Evas_Coord fw, fh, fy;
8676 /* If there are no text items yet, calc ascent/descent
8677 * according to the current format. */
8678 if (it && (ascent + descent == 0))
8679 _layout_format_ascent_descent_adjust(obj, &ascent,
8680 &descent, it->format);
8682 _layout_calculate_format_item_size(obj, fi, &ascent,
8683 &descent, &fy, &fw, &fh);
8688 _layout_format_ascent_descent_adjust(obj, &ascent,
8689 &descent, it->format);
8693 _size_native_calc_line_finalize(obj, line_items, &ascent, &descent, &w);
8695 line_items = eina_list_free(line_items);
8697 /* Do the last addition */
8698 if (ascent + descent > h)
8699 h = ascent + descent;
8709 evas_object_textblock_size_native_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
8712 if (!o->native.valid)
8714 Evas_Coord wmax = 0, hmax = 0;
8715 Evas_Object_Textblock_Paragraph *par;
8716 /* We just want the layout objects to update, should probably
8718 if (!o->formatted.valid) _relayout(obj);
8719 EINA_INLIST_FOREACH(o->paragraphs, par)
8722 _size_native_calc_paragraph_size(obj, o, par, &tw, &th);
8731 o->native.valid = 1;
8732 o->content_changed = 0;
8733 o->format_changed = EINA_FALSE;
8735 if (w) *w = o->native.w;
8736 if (h) *h = o->native.h;
8740 evas_object_textblock_style_insets_get(const Evas_Object *obj, Evas_Coord *l, Evas_Coord *r, Evas_Coord *t, Evas_Coord *b)
8743 if (!o->formatted.valid) _relayout(obj);
8744 if (l) *l = o->style_pad.l;
8745 if (r) *r = o->style_pad.r;
8746 if (t) *t = o->style_pad.t;
8747 if (b) *b = o->style_pad.b;
8751 * FIXME: DELETE ME! DELETE ME!
8752 * This is an ugly workaround to get around the fact that
8753 * evas_object_textblock_coords_recalc isn't really called when it's supposed
8754 * to. When that bug is fixed please remove this. */
8756 _workaround_object_coords_recalc(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
8758 evas_object_textblock_coords_recalc(obj);
8761 /* all nice and private */
8763 evas_object_textblock_init(Evas_Object *obj)
8765 Evas_Object_Textblock *o;
8766 #ifdef HAVE_LINEBREAK
8767 static Eina_Bool linebreak_init = EINA_FALSE;
8768 if (!linebreak_init)
8770 linebreak_init = EINA_TRUE;
8775 /* alloc image ob, setup methods and default values */
8776 obj->object_data = evas_object_textblock_new();
8777 /* set up default settings for this kind of object */
8778 obj->cur.color.r = 255;
8779 obj->cur.color.g = 255;
8780 obj->cur.color.b = 255;
8781 obj->cur.color.a = 255;
8782 obj->cur.geometry.x = 0.0;
8783 obj->cur.geometry.y = 0.0;
8784 obj->cur.geometry.w = 0.0;
8785 obj->cur.geometry.h = 0.0;
8787 /* set up object-specific settings */
8788 obj->prev = obj->cur;
8789 /* set up methods (compulsory) */
8790 obj->func = &object_func;
8793 o = (Evas_Object_Textblock *)(obj->object_data);
8794 o->cursor->obj = obj;
8795 o->legacy_newline = EINA_TRUE;
8796 evas_object_event_callback_priority_add(obj, EVAS_CALLBACK_RESIZE, -1000,
8797 _workaround_object_coords_recalc, NULL);
8801 evas_object_textblock_new(void)
8803 Evas_Object_Textblock *o;
8805 /* alloc obj private data */
8806 EVAS_MEMPOOL_INIT(_mp_obj, "evas_object_textblock", Evas_Object_Textblock, 64, NULL);
8807 o = EVAS_MEMPOOL_ALLOC(_mp_obj, Evas_Object_Textblock);
8808 if (!o) return NULL;
8809 EVAS_MEMPOOL_PREP(_mp_obj, o, Evas_Object_Textblock);
8810 o->magic = MAGIC_OBJ_TEXTBLOCK;
8811 o->cursor = calloc(1, sizeof(Evas_Textblock_Cursor));
8812 _format_command_init();
8817 evas_object_textblock_free(Evas_Object *obj)
8819 Evas_Object_Textblock *o;
8821 evas_object_textblock_clear(obj);
8822 evas_object_textblock_style_set(obj, NULL);
8823 o = (Evas_Object_Textblock *)(obj->object_data);
8827 Evas_Textblock_Cursor *cur;
8829 cur = (Evas_Textblock_Cursor *)o->cursors->data;
8830 o->cursors = eina_list_remove_list(o->cursors, o->cursors);
8833 if (o->repch) eina_stringshare_del(o->repch);
8834 if (o->ellip_ti) _item_free(obj, NULL, _ITEM(o->ellip_ti));
8836 EVAS_MEMPOOL_FREE(_mp_obj, o);
8837 _format_command_shutdown();
8842 evas_object_textblock_render(Evas_Object *obj, void *output, void *context, void *surface, int x, int y)
8844 Evas_Object_Textblock_Paragraph *par, *start = NULL;
8845 Evas_Object_Textblock_Line *ln;
8846 Evas_Object_Textblock *o;
8848 unsigned char r = 0, g = 0, b = 0, a = 0;
8849 unsigned char r2 = 0, g2 = 0, b2 = 0, a2 = 0;
8850 unsigned char r3 = 0, g3 = 0, b3 = 0, a3 = 0;
8851 int cx, cy, cw, ch, clip;
8852 const char vals[5][5] =
8861 /* render object to surface with context, and offxet by x,y */
8862 o = (Evas_Object_Textblock *)(obj->object_data);
8863 obj->layer->evas->engine.func->context_multiplier_unset(output,
8865 /* FIXME: This clipping is just until we fix inset handling correctly. */
8866 ENFN->context_clip_clip(output, context,
8867 obj->cur.geometry.x + x,
8868 obj->cur.geometry.y + y,
8869 obj->cur.geometry.w,
8870 obj->cur.geometry.h);
8871 clip = ENFN->context_clip_get(output, context, &cx, &cy, &cw, &ch);
8872 /* If there are no paragraphs and thus there are no lines,
8873 * there's nothing left to do. */
8874 if (!o->paragraphs) return;
8876 #define ITEM_WALK() \
8877 EINA_INLIST_FOREACH(start, par) \
8879 if (!par->visible) continue; \
8882 if ((obj->cur.geometry.y + y + par->y + par->h) < (cy - 20)) \
8884 if ((obj->cur.geometry.y + y + par->y) > (cy + ch + 20)) \
8887 _layout_paragraph_render(o, par); \
8888 EINA_INLIST_FOREACH(par->lines, ln) \
8890 Evas_Object_Textblock_Item *itr; \
8894 if ((obj->cur.geometry.y + y + par->y + ln->y + ln->h) < (cy - 20)) \
8896 if ((obj->cur.geometry.y + y + par->y + ln->y) > (cy + ch + 20)) \
8899 EINA_INLIST_FOREACH(ln->items, itr) \
8902 yoff = ln->baseline; \
8903 if (itr->format->valign != -1.0) \
8905 yoff += itr->format->valign * (ln->h - itr->h); \
8909 if ((obj->cur.geometry.x + x + ln->x + itr->x + itr->w) < (cx - 20)) \
8911 if ((obj->cur.geometry.x + x + ln->x + itr->x) > (cx + cw + 20)) \
8914 if ((ln->x + itr->x + itr->w) <= 0) continue; \
8915 if (ln->x + itr->x > obj->cur.geometry.w) break; \
8918 #define ITEM_WALK_END() \
8924 #define COLOR_SET(col) \
8925 ENFN->context_color_set(output, context, \
8926 (obj->cur.cache.clip.r * ti->parent.format->color.col.r) / 255, \
8927 (obj->cur.cache.clip.g * ti->parent.format->color.col.g) / 255, \
8928 (obj->cur.cache.clip.b * ti->parent.format->color.col.b) / 255, \
8929 (obj->cur.cache.clip.a * ti->parent.format->color.col.a) / 255);
8930 #define COLOR_SET_AMUL(col, amul) \
8931 ENFN->context_color_set(output, context, \
8932 (obj->cur.cache.clip.r * ti->parent.format->color.col.r * (amul)) / 65025, \
8933 (obj->cur.cache.clip.g * ti->parent.format->color.col.g * (amul)) / 65025, \
8934 (obj->cur.cache.clip.b * ti->parent.format->color.col.b * (amul)) / 65025, \
8935 (obj->cur.cache.clip.a * ti->parent.format->color.col.a * (amul)) / 65025);
8936 #define DRAW_TEXT(ox, oy) \
8937 if (ti->parent.format->font.font) ENFN->font_draw(output, context, surface, ti->parent.format->font.font, \
8938 obj->cur.geometry.x + ln->x + ti->parent.x + x + (ox), \
8939 obj->cur.geometry.y + ln->par->y + ln->y + yoff + y + (oy), \
8940 ti->parent.w, ti->parent.h, ti->parent.w, ti->parent.h, \
8944 #define DRAW_RECT(ox, oy, ow, oh, or, og, ob, oa) \
8947 ENFN->context_color_set(output, \
8949 (obj->cur.cache.clip.r * or) / 255, \
8950 (obj->cur.cache.clip.g * og) / 255, \
8951 (obj->cur.cache.clip.b * ob) / 255, \
8952 (obj->cur.cache.clip.a * oa) / 255); \
8953 ENFN->rectangle_draw(output, \
8956 obj->cur.geometry.x + ln->x + x + (ox), \
8957 obj->cur.geometry.y + ln->par->y + ln->y + y + (oy), \
8963 #define DRAW_FORMAT(oname, oy, oh, or, og, ob, oa) \
8966 if (itr->format->oname) \
8968 or = itr->format->color.oname.r; \
8969 og = itr->format->color.oname.g; \
8970 ob = itr->format->color.oname.b; \
8971 oa = itr->format->color.oname.a; \
8972 if (!EINA_INLIST_GET(itr)->next) \
8974 DRAW_RECT(itr->x, oy, itr->w, oh, or, og, ob, oa); \
8978 DRAW_RECT(itr->x, oy, itr->adv, oh, or, og, ob, oa); \
8985 Evas_Coord look_for_y = 0 - (obj->cur.geometry.y + y);
8988 Evas_Coord tmp_lfy = cy - (obj->cur.geometry.y + y);
8989 if (tmp_lfy > look_for_y)
8990 look_for_y = tmp_lfy;
8993 if (look_for_y >= 0)
8994 start = _layout_find_paragraph_by_y(o, look_for_y);
8997 start = o->paragraphs;
9002 DRAW_FORMAT(backing, 0, ln->h, r, g, b, a);
9006 /* There are size adjustments that depend on the styles drawn here back
9007 * in "_text_item_update_sizes" should not modify one without the other. */
9009 /* prepare everything for text draw */
9014 int shad_dst, shad_sz, dx, dy, haveshad;
9015 Evas_Object_Textblock_Text_Item *ti;
9016 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9019 shad_dst = shad_sz = dx = dy = haveshad = 0;
9020 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC)
9022 case EVAS_TEXT_STYLE_SHADOW:
9023 case EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW:
9027 case EVAS_TEXT_STYLE_OUTLINE_SHADOW:
9028 case EVAS_TEXT_STYLE_FAR_SHADOW:
9032 case EVAS_TEXT_STYLE_FAR_SOFT_SHADOW:
9037 case EVAS_TEXT_STYLE_SOFT_SHADOW:
9049 switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_SHADOW_DIRECTION)
9051 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT:
9055 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM:
9059 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT:
9063 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT:
9067 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT:
9071 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP:
9075 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT:
9079 case EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT:
9095 for (j = 0; j < 5; j++)
9097 for (i = 0; i < 5; i++)
9099 if (vals[i][j] != 0)
9101 COLOR_SET_AMUL(shadow, vals[i][j] * 50);
9102 DRAW_TEXT(i - 2 + dx, j - 2 + dy);
9117 Evas_Object_Textblock_Text_Item *ti;
9118 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9121 if (ti->parent.format->style == EVAS_TEXT_STYLE_GLOW)
9123 for (j = 0; j < 5; j++)
9125 for (i = 0; i < 5; i++)
9127 if (vals[i][j] != 0)
9129 COLOR_SET_AMUL(glow, vals[i][j] * 50);
9130 DRAW_TEXT(i - 2, j - 2);
9146 Evas_Object_Textblock_Text_Item *ti;
9147 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9150 if ((ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE) ||
9151 (ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE_SHADOW) ||
9152 (ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW))
9160 else if (ti->parent.format->style == EVAS_TEXT_STYLE_SOFT_OUTLINE)
9162 for (j = 0; j < 5; j++)
9164 for (i = 0; i < 5; i++)
9166 if (((i != 2) || (j != 2)) && (vals[i][j] != 0))
9168 COLOR_SET_AMUL(outline, vals[i][j] * 50);
9169 DRAW_TEXT(i - 2, j - 2);
9177 /* normal text and lines */
9180 Evas_Object_Textblock_Text_Item *ti;
9181 ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9190 DRAW_FORMAT(strikethrough, (ln->h / 2), 1, r, g, b, a);
9193 DRAW_FORMAT(underline, ln->baseline + 1, 1, r2, g2, b2, a2);
9196 DRAW_FORMAT(underline2, ln->baseline + 3, 1, r3, g3, b3, a3);
9202 evas_object_textblock_render_pre(Evas_Object *obj)
9204 Evas_Object_Textblock *o;
9207 /* dont pre-render the obj twice! */
9208 if (obj->pre_render_done) return;
9209 obj->pre_render_done = 1;
9210 /* pre-render phase. this does anything an object needs to do just before */
9211 /* rendering. this could mean loading the image data, retrieving it from */
9212 /* elsewhere, decoding video etc. */
9213 /* then when this is done the object needs to figure if it changed and */
9214 /* if so what and where and add the appropriate redraw textblocks */
9215 o = (Evas_Object_Textblock *)(obj->object_data);
9216 if ((o->changed) || (o->content_changed) || (o->format_changed) ||
9217 ((obj->cur.geometry.w != o->last_w) ||
9218 (((o->valign != 0.0) || (o->have_ellipsis)) &&
9219 (obj->cur.geometry.h != o->last_h))))
9223 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9224 is_v = evas_object_is_visible(obj);
9225 was_v = evas_object_was_visible(obj);
9231 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9232 is_v = evas_object_is_visible(obj);
9233 was_v = evas_object_was_visible(obj);
9236 /* if someone is clipping this obj - go calculate the clipper */
9237 if (obj->cur.clipper)
9239 if (obj->cur.cache.clip.dirty)
9240 evas_object_clip_recalc(obj->cur.clipper);
9241 obj->cur.clipper->func->render_pre(obj->cur.clipper);
9243 /* now figure what changed and add draw rects */
9244 /* if it just became visible or invisible */
9245 is_v = evas_object_is_visible(obj);
9246 was_v = evas_object_was_visible(obj);
9249 evas_object_render_pre_visible_change(&obj->layer->evas->clip_changes, obj, is_v, was_v);
9252 if ((obj->cur.map != obj->prev.map) ||
9253 (obj->cur.usemap != obj->prev.usemap))
9255 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9258 /* it's not visible - we accounted for it appearing or not so just abort */
9259 if (!is_v) goto done;
9260 /* clipper changed this is in addition to anything else for obj */
9261 evas_object_render_pre_clipper_change(&obj->layer->evas->clip_changes, obj);
9262 /* if we restacked (layer or just within a layer) and don't clip anyone */
9265 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9268 /* if it changed color */
9269 if ((obj->cur.color.r != obj->prev.color.r) ||
9270 (obj->cur.color.g != obj->prev.color.g) ||
9271 (obj->cur.color.b != obj->prev.color.b) ||
9272 (obj->cur.color.a != obj->prev.color.a))
9274 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9277 /* if it changed geometry - and obviously not visibility or color */
9278 /* calculate differences since we have a constant color fill */
9279 /* we really only need to update the differences */
9280 if ((obj->cur.geometry.x != obj->prev.geometry.x) ||
9281 (obj->cur.geometry.y != obj->prev.geometry.y) ||
9282 (obj->cur.geometry.w != obj->prev.geometry.w) ||
9283 (obj->cur.geometry.h != obj->prev.geometry.h))
9285 evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9289 evas_object_render_pre_effect_updates(&obj->layer->evas->clip_changes, obj, is_v, was_v);
9293 evas_object_textblock_render_post(Evas_Object *obj)
9295 /* Evas_Object_Textblock *o; */
9297 /* this moves the current data to the previous state parts of the object */
9298 /* in whatever way is safest for the object. also if we don't need object */
9299 /* data anymore we can free it if the object deems this is a good idea */
9300 /* o = (Evas_Object_Textblock *)(obj->object_data); */
9301 /* remove those pesky changes */
9302 evas_object_clip_changes_clean(obj);
9303 /* move cur to prev safely for object data */
9304 obj->prev = obj->cur;
9305 /* o->prev = o->cur; */
9306 /* o->changed = 0; */
9309 static unsigned int evas_object_textblock_id_get(Evas_Object *obj)
9311 Evas_Object_Textblock *o;
9313 o = (Evas_Object_Textblock *)(obj->object_data);
9315 return MAGIC_OBJ_TEXTBLOCK;
9318 static unsigned int evas_object_textblock_visual_id_get(Evas_Object *obj)
9320 Evas_Object_Textblock *o;
9322 o = (Evas_Object_Textblock *)(obj->object_data);
9324 return MAGIC_OBJ_CUSTOM;
9327 static void *evas_object_textblock_engine_data_get(Evas_Object *obj)
9329 Evas_Object_Textblock *o;
9331 o = (Evas_Object_Textblock *)(obj->object_data);
9332 if (!o) return NULL;
9333 return o->engine_data;
9337 evas_object_textblock_is_opaque(Evas_Object *obj __UNUSED__)
9339 /* this returns 1 if the internal object data implies that the object is */
9340 /* currently fulyl opque over the entire gradient it occupies */
9345 evas_object_textblock_was_opaque(Evas_Object *obj __UNUSED__)
9347 /* this returns 1 if the internal object data implies that the object was */
9348 /* currently fulyl opque over the entire gradient it occupies */
9353 evas_object_textblock_coords_recalc(Evas_Object *obj)
9355 Evas_Object_Textblock *o;
9357 o = (Evas_Object_Textblock *)(obj->object_data);
9358 if ((obj->cur.geometry.w != o->last_w) ||
9359 (((o->valign != 0.0) || (o->have_ellipsis)) &&
9360 (obj->cur.geometry.h != o->last_h)))
9362 o->formatted.valid = 0;
9368 evas_object_textblock_scale_update(Evas_Object *obj)
9370 Evas_Object_Textblock *o;
9372 o = (Evas_Object_Textblock *)(obj->object_data);
9373 _evas_textblock_invalidate_all(o);
9374 _evas_textblock_changed(o, obj);
9378 _evas_object_textblock_rehint(Evas_Object *obj)
9380 Evas_Object_Textblock *o;
9381 Evas_Object_Textblock_Paragraph *par;
9382 Evas_Object_Textblock_Line *ln;
9384 o = (Evas_Object_Textblock *)(obj->object_data);
9385 EINA_INLIST_FOREACH(o->paragraphs, par)
9387 EINA_INLIST_FOREACH(par->lines, ln)
9389 Evas_Object_Textblock_Item *it;
9391 EINA_INLIST_FOREACH(ln->items, it)
9393 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
9395 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
9396 if (ti->parent.format->font.font)
9398 #ifdef EVAS_FRAME_QUEUING
9399 evas_common_pipe_op_text_flush((RGBA_Font *) ti->parent.format->font.font);
9401 evas_font_load_hinting_set(obj->layer->evas,
9402 ti->parent.format->font.font,
9403 obj->layer->evas->hinting);
9409 _evas_textblock_invalidate_all(o);
9410 _evas_textblock_changed(o, obj);
9418 /* return EINA_FALSE on error, used in unit_testing */
9420 _evas_textblock_check_item_node_link(Evas_Object *obj)
9422 Evas_Object_Textblock *o;
9423 Evas_Object_Textblock_Paragraph *par;
9424 Evas_Object_Textblock_Line *ln;
9425 Evas_Object_Textblock_Item *it;
9427 o = (Evas_Object_Textblock *)(obj->object_data);
9428 if (!o) return EINA_FALSE;
9430 if (!o->formatted.valid) _relayout(obj);
9432 EINA_INLIST_FOREACH(o->paragraphs, par)
9434 EINA_INLIST_FOREACH(par->lines, ln)
9436 EINA_INLIST_FOREACH(ln->items, it)
9438 if (it->text_node != par->text_node)
9447 _evas_textblock_format_offset_get(const Evas_Object_Textblock_Node_Format *n)
9454 /* Good for debugging */
9456 pfnode(Evas_Object_Textblock_Node_Format *n)
9458 printf("Format Node: %p\n", n);
9459 printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
9460 printf("text_node = %p, offset = %u, visible = %d\n", n->text_node, n->offset, n->visible);
9461 printf("'%s'\n", eina_strbuf_string_get(n->format));
9465 ptnode(Evas_Object_Textblock_Node_Text *n)
9467 printf("Text Node: %p\n", n);
9468 printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
9469 printf("format_node = %p\n", n->format_node);
9470 printf("'%ls'\n", eina_ustrbuf_string_get(n->unicode));
9474 pitem(Evas_Object_Textblock_Item *it)
9476 Evas_Object_Textblock_Text_Item *ti;
9477 Evas_Object_Textblock_Format_Item *fi;
9478 printf("Item: %p\n", it);
9479 printf("Type: %s (%d)\n", (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
9480 "TEXT" : "FORMAT", it->type);
9481 printf("Text pos: %d Visual pos: %d\n", it->text_pos,
9488 printf("Coords: x = %d w = %d adv = %d\n", (int) it->x, (int) it->w,
9490 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
9492 ti = _ITEM_TEXT(it);
9493 printf("Text: '%*ls'\n", ti->text_props.text_len, GET_ITEM_TEXT(ti));
9497 fi = _ITEM_FORMAT(it);
9498 printf("Format: '%s'\n", fi->item);
9503 ppar(Evas_Object_Textblock_Paragraph *par)
9505 Evas_Object_Textblock_Item *it;
9507 EINA_LIST_FOREACH(par->logical_items, i, it)
9509 printf("***********************\n");