0426bf367ba671900072087d4a9fc669e414d858
[profile/ivi/evas.git] / src / lib / canvas / evas_object_textblock.c
1 /**
2  * @internal
3  * @section Evas_Object_Textblock_Internal Internal Textblock Object Tutorial
4  *
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.".
7  *
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).
13  *
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.
19  *
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.
34  *
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
39  * commands.
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
48  *
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
56  * purposes.
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.
60  *
61  * @subsection textblock_layout The layout system
62  * @todo write @ref textblock_layout
63  */
64 #include "evas_common.h"
65 #include "evas_private.h"
66 #include <stdlib.h>
67
68 #ifdef HAVE_LINEBREAK
69 #include "linebreak.h"
70 #include "wordbreak.h"
71 #endif
72
73 /* save typing */
74 #define ENFN obj->layer->evas->engine.func
75 #define ENDT obj->layer->evas->engine.data.output
76
77 /* private magic number for textblock objects */
78 static const char o_type[] = "textblock";
79
80 /* The char to be inserted instead of visible formats */
81 #define _REPLACEMENT_CHAR 0xFFFC
82 #define _PARAGRAPH_SEPARATOR 0x2029
83 #define _NEWLINE '\n'
84 #define _TAB '\t'
85
86 #define _REPLACEMENT_CHAR_UTF8 "\xEF\xBF\xBC"
87 #define _PARAGRAPH_SEPARATOR_UTF8 "\xE2\x80\xA9"
88 #define _NEWLINE_UTF8 "\n"
89 #define _TAB_UTF8 "\t"
90 #define EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(ch) \
91    (((ch) == _REPLACEMENT_CHAR) || \
92     ((ch) ==  _NEWLINE) || \
93     ((ch) == _TAB) || \
94     ((ch) == _PARAGRAPH_SEPARATOR))
95
96 #ifdef CRITICAL
97 #undef CRITICAL
98 #endif
99 #define CRITICAL(...) EINA_LOG_DOM_CRIT(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__)
100
101 #ifdef ERR
102 #undef ERR
103 #endif
104 #define ERR(...) EINA_LOG_DOM_ERR(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__)
105
106 #ifdef WRN
107 #undef WRN
108 #endif
109 #define WRN(...) EINA_LOG_DOM_WARN(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__)
110
111 #ifdef INF
112 #undef INF
113 #endif
114 #define INF(...) EINA_LOG_DOM_INFO(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__)
115
116 #ifdef DBG
117 #undef DBG
118 #endif
119 #define DBG(...) EINA_LOG_DOM_DBG(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__)
120
121 #define TB_NULL_CHECK(null_check, ...) \
122    do \
123      { \
124         if (!null_check) \
125           { \
126              ERR("%s is NULL while it shouldn't be, please notify developers.", #null_check); \
127              return __VA_ARGS__; \
128           } \
129      } \
130    while(0)
131
132 /* private struct for textblock object internal data */
133 /**
134  * @internal
135  * @typedef Evas_Object_Textblock
136  * The actual textblock object.
137  */
138 typedef struct _Evas_Object_Textblock             Evas_Object_Textblock;
139 /**
140  * @internal
141  * @typedef Evas_Object_Style_Tag
142  * The structure used for finding style tags.
143  */
144 typedef struct _Evas_Object_Style_Tag             Evas_Object_Style_Tag;
145 /**
146  * @internal
147  * @typedef Evas_Object_Style_Tag
148  * The structure used for finding style tags.
149  */
150 typedef struct _Evas_Object_Style_Tag_Base        Evas_Object_Style_Tag_Base;
151 /**
152  * @internal
153  * @typedef Evas_Object_Textblock_Node_Text
154  * A text node.
155  */
156 typedef struct _Evas_Object_Textblock_Node_Text   Evas_Object_Textblock_Node_Text;
157 /*
158  * Defined in Evas.h
159 typedef struct _Evas_Object_Textblock_Node_Format Evas_Object_Textblock_Node_Format;
160 */
161
162 /**
163  * @internal
164  * @typedef Evas_Object_Textblock_Paragraph
165  * A layouting paragraph.
166  */
167 typedef struct _Evas_Object_Textblock_Paragraph   Evas_Object_Textblock_Paragraph;
168 /**
169  * @internal
170  * @typedef Evas_Object_Textblock_Line
171  * A layouting line.
172  */
173 typedef struct _Evas_Object_Textblock_Line        Evas_Object_Textblock_Line;
174 /**
175  * @internal
176  * @typedef Evas_Object_Textblock_Item
177  * A layouting item.
178  */
179 typedef struct _Evas_Object_Textblock_Item        Evas_Object_Textblock_Item;
180 /**
181  * @internal
182  * @typedef Evas_Object_Textblock_Item
183  * A layouting text item.
184  */
185 typedef struct _Evas_Object_Textblock_Text_Item        Evas_Object_Textblock_Text_Item;
186 /**
187  * @internal
188  * @typedef Evas_Object_Textblock_Format_Item
189  * A layouting format item.
190  */
191 typedef struct _Evas_Object_Textblock_Format_Item Evas_Object_Textblock_Format_Item;
192 /**
193  * @internal
194  * @typedef Evas_Object_Textblock_Format
195  * A textblock format.
196  */
197 typedef struct _Evas_Object_Textblock_Format      Evas_Object_Textblock_Format;
198
199 /**
200  * @internal
201  * @def IS_AT_END(ti, ind)
202  * Return true if ind is at the end of the text item, false otherwise.
203  */
204 #define IS_AT_END(ti, ind) (ind == ti->text_props.text_len)
205
206 /**
207  * @internal
208  * @def MOVE_PREV_UNTIL(limit, ind)
209  * This decrements ind as long as ind > limit.
210  */
211 #define MOVE_PREV_UNTIL(limit, ind) \
212    do \
213      { \
214         if ((limit) < (ind)) \
215            (ind)--; \
216      } \
217    while (0)
218
219 /**
220  * @internal
221  * @def MOVE_NEXT_UNTIL(limit, ind)
222  * This increments ind as long as ind < limit
223  */
224 #define MOVE_NEXT_UNTIL(limit, ind) \
225    do \
226      { \
227         if ((ind) < (limit)) \
228            (ind)++; \
229      } \
230    while (0)
231
232 /**
233  * @internal
234  * @def GET_ITEM_TEXT(ti)
235  * Returns a const reference to the text of the ti (not null terminated).
236  */
237 #define GET_ITEM_TEXT(ti) \
238    (((ti)->parent.text_node) ? \
239     (eina_ustrbuf_string_get((ti)->parent.text_node->unicode) + \
240       (ti)->parent.text_pos) : EINA_UNICODE_EMPTY_STRING)
241 /**
242  * @internal
243  * @def _FORMAT_IS_CLOSER_OF(base, closer, closer_len)
244  * Returns true if closer is the closer of base.
245  */
246 #define _FORMAT_IS_CLOSER_OF(base, closer, closer_len) \
247    (!strncmp(base, closer, closer_len) && \
248     (!base[closer_len] || \
249      (base[closer_len] == '=') || \
250      _is_white(base[closer_len])))
251
252 /*FIXME: document the structs and struct items. */
253 struct _Evas_Object_Style_Tag_Base
254 {
255    char *tag;
256    char *replace;
257    size_t tag_len;
258    size_t replace_len;
259 };
260
261 struct _Evas_Object_Style_Tag
262 {
263    EINA_INLIST;
264    Evas_Object_Style_Tag_Base tag;
265 };
266
267 struct _Evas_Object_Textblock_Node_Text
268 {
269    EINA_INLIST;
270    Eina_UStrbuf                       *unicode;
271    char                               *utf8;
272    Evas_Object_Textblock_Node_Format  *format_node;
273    Evas_Object_Textblock_Paragraph    *par;
274    Eina_Bool                           dirty : 1;
275    Eina_Bool                           is_new : 1;
276 };
277
278 struct _Evas_Object_Textblock_Node_Format
279 {
280    EINA_INLIST;
281    const char                         *format;
282    const char                         *orig_format;
283    Evas_Object_Textblock_Node_Text    *text_node;
284    size_t                              offset;
285    unsigned char                       anchor : 2;
286    Eina_Bool                           opener : 1;
287    Eina_Bool                           own_closer : 1;
288    Eina_Bool                           visible : 1;
289    Eina_Bool                           format_change : 1;
290    Eina_Bool                           is_new : 1;
291 };
292
293 /* The default tags to use */
294 static const Evas_Object_Style_Tag_Base default_tags[] = {
295           { "b", "+ font_weight=Bold", 1, 18 },
296           { "i", "+ font_style=Italic", 1, 19 }};
297
298 #define ANCHOR_NONE 0
299 #define ANCHOR_A 1
300 #define ANCHOR_ITEM 2
301
302 /**
303  * @internal
304  * @def _NODE_TEXT(x)
305  * A convinience macro for casting to a text node.
306  */
307 #define _NODE_TEXT(x)  ((Evas_Object_Textblock_Node_Text *) (x))
308 /**
309  * @internal
310  * @def _NODE_FORMAT(x)
311  * A convinience macro for casting to a format node.
312  */
313 #define _NODE_FORMAT(x)  ((Evas_Object_Textblock_Node_Format *) (x))
314 /**
315  * @internal
316  * @def _ITEM(x)
317  * A convinience macro for casting to a generic item.
318  */
319 #define _ITEM(x)  ((Evas_Object_Textblock_Item *) (x))
320 /**
321  * @internal
322  * @def _ITEM_TEXT(x)
323  * A convinience macro for casting to a text item.
324  */
325 #define _ITEM_TEXT(x)  ((Evas_Object_Textblock_Text_Item *) (x))
326 /**
327  * @internal
328  * @def _ITEM_FORMAT(x)
329  * A convinience macro for casting to a format item.
330  */
331 #define _ITEM_FORMAT(x)  ((Evas_Object_Textblock_Format_Item *) (x))
332
333 struct _Evas_Object_Textblock_Paragraph
334 {
335    EINA_INLIST;
336    Evas_Object_Textblock_Line        *lines;
337    Evas_Object_Textblock_Node_Text   *text_node;
338    Eina_List                         *logical_items;
339    Evas_BiDi_Paragraph_Props         *bidi_props; /* Only valid during layout */
340    Evas_BiDi_Direction                direction;
341    Evas_Coord                         y, w, h;
342    int                                line_no;
343    Eina_Bool                          is_bidi : 1;
344    Eina_Bool                          visible : 1;
345    Eina_Bool                          rendered : 1;
346 };
347
348 struct _Evas_Object_Textblock_Line
349 {
350    EINA_INLIST;
351    Evas_Object_Textblock_Item        *items;
352    Evas_Object_Textblock_Paragraph   *par;
353    Evas_Coord                         x, y, w, h;
354    int                                baseline;
355    int                                line_no;
356 };
357
358 typedef enum _Evas_Textblock_Item_Type
359 {
360    EVAS_TEXTBLOCK_ITEM_TEXT,
361    EVAS_TEXTBLOCK_ITEM_FORMAT,
362 } Evas_Textblock_Item_Type;
363
364 struct _Evas_Object_Textblock_Item
365 {
366    EINA_INLIST;
367    Evas_Textblock_Item_Type             type;
368    Evas_Object_Textblock_Node_Text     *text_node;
369    Evas_Object_Textblock_Format        *format;
370    size_t                               text_pos;
371 #ifdef BIDI_SUPPORT
372    size_t                               visual_pos;
373 #endif
374    Evas_Coord                           adv, x, w, h;
375    Eina_Bool                            merge : 1; /* Indicates whether this
376                                                       item should merge to the
377                                                       previous item or not */
378    Eina_Bool                            visually_deleted : 1;
379                                                    /* Indicates whether this
380                                                       item is used in the visual
381                                                       layout or not. */
382 };
383
384 struct _Evas_Object_Textblock_Text_Item
385 {
386    Evas_Object_Textblock_Item       parent;
387    Evas_Text_Props                  text_props;
388    Evas_Coord                       inset;
389    Evas_Coord                       x_adjustment; /* Used to indicate by how
390                                                      much we adjusted sizes */
391 };
392
393 struct _Evas_Object_Textblock_Format_Item
394 {
395    Evas_Object_Textblock_Item           parent;
396    Evas_BiDi_Direction                  bidi_dir;
397    const char                          *item;
398    int                                  y;
399    unsigned char                        vsize : 2;
400    unsigned char                        size : 2;
401    Eina_Bool                            formatme : 1;
402 };
403
404 struct _Evas_Object_Textblock_Format
405 {
406    Evas_Object_Textblock_Node_Format *fnode;
407    double               halign;
408    double               valign;
409    struct {
410       Evas_Font_Description *fdesc;
411       const char       *source;
412       Evas_Font_Set    *font;
413       Evas_Font_Size    size;
414    } font;
415    struct {
416       struct {
417          unsigned char  r, g, b, a;
418       } normal, underline, underline2, underline_dash, outline, shadow, glow, glow2, backing,
419         strikethrough;
420    } color;
421    struct {
422       int               l, r;
423    } margin;
424    int                  ref;
425    int                  tabstops;
426    int                  linesize;
427    int                  linegap;
428    int                  underline_dash_width;
429    int                  underline_dash_gap;
430    double               linerelsize;
431    double               linerelgap;
432    double               linefill;
433    double               ellipsis;
434    unsigned char        style;
435    Eina_Bool            wrap_word : 1;
436    Eina_Bool            wrap_char : 1;
437    Eina_Bool            wrap_mixed : 1;
438    Eina_Bool            underline : 1;
439    Eina_Bool            underline2 : 1;
440    Eina_Bool            underline_dash : 1;
441    Eina_Bool            strikethrough : 1;
442    Eina_Bool            backing : 1;
443    Eina_Bool            password : 1;
444    Eina_Bool            halign_auto : 1;
445 };
446
447 struct _Evas_Textblock_Style
448 {
449    const char            *style_text;
450    char                  *default_tag;
451    Evas_Object_Style_Tag *tags;
452    Eina_List             *objects;
453    Eina_Bool              delete_me : 1;
454 };
455
456 struct _Evas_Textblock_Cursor
457 {
458    Evas_Object                     *obj;
459    size_t                           pos;
460    Evas_Object_Textblock_Node_Text *node;
461 };
462
463 /* Size of the index array */
464 #define TEXTBLOCK_PAR_INDEX_SIZE 10
465 struct _Evas_Object_Textblock
466 {
467    DATA32                              magic;
468    Evas_Textblock_Style               *style;
469    Evas_Textblock_Style               *style_user;
470    Evas_Textblock_Cursor              *cursor;
471    Eina_List                          *cursors;
472    Evas_Object_Textblock_Node_Text    *text_nodes;
473    Evas_Object_Textblock_Node_Format  *format_nodes;
474
475    int                                 num_paragraphs;
476    Evas_Object_Textblock_Paragraph    *paragraphs;
477    Evas_Object_Textblock_Paragraph    *par_index[TEXTBLOCK_PAR_INDEX_SIZE];
478
479    Evas_Object_Textblock_Text_Item    *ellip_ti;
480    Eina_List                          *anchors_a;
481    Eina_List                          *anchors_item;
482    int                                 last_w, last_h;
483    struct {
484       int                              l, r, t, b;
485    } style_pad;
486    double                              valign;
487    char                               *markup_text;
488    void                               *engine_data;
489    const char                         *repch;
490    const char                         *bidi_delimiters;
491    struct {
492       int                              w, h;
493       Eina_Bool                        valid : 1;
494    } formatted, native;
495    Eina_Bool                           redraw : 1;
496    Eina_Bool                           changed : 1;
497    Eina_Bool                           content_changed : 1;
498    Eina_Bool                           format_changed : 1;
499    Eina_Bool                           have_ellipsis : 1;
500    Eina_Bool                           legacy_newline : 1;
501 };
502
503 /* private methods for textblock objects */
504 static void evas_object_textblock_init(Evas_Object *obj);
505 static void *evas_object_textblock_new(void);
506 static void evas_object_textblock_render(Evas_Object *obj, void *output, void *context, void *surface, int x, int y);
507 static void evas_object_textblock_free(Evas_Object *obj);
508 static void evas_object_textblock_render_pre(Evas_Object *obj);
509 static void evas_object_textblock_render_post(Evas_Object *obj);
510
511 static unsigned int evas_object_textblock_id_get(Evas_Object *obj);
512 static unsigned int evas_object_textblock_visual_id_get(Evas_Object *obj);
513 static void *evas_object_textblock_engine_data_get(Evas_Object *obj);
514
515 static int evas_object_textblock_is_opaque(Evas_Object *obj);
516 static int evas_object_textblock_was_opaque(Evas_Object *obj);
517
518 static void evas_object_textblock_coords_recalc(Evas_Object *obj);
519
520 static void evas_object_textblock_scale_update(Evas_Object *obj);
521
522 static const Evas_Object_Func object_func =
523 {
524    /* methods (compulsory) */
525    evas_object_textblock_free,
526      evas_object_textblock_render,
527      evas_object_textblock_render_pre,
528      evas_object_textblock_render_post,
529      evas_object_textblock_id_get,
530      evas_object_textblock_visual_id_get,
531      evas_object_textblock_engine_data_get,
532    /* these are optional. NULL = nothing */
533      NULL,
534      NULL,
535      NULL,
536      NULL,
537      evas_object_textblock_is_opaque,
538      evas_object_textblock_was_opaque,
539      NULL,
540      NULL,
541      evas_object_textblock_coords_recalc,
542      evas_object_textblock_scale_update,
543      NULL,
544      NULL,
545      NULL
546 };
547
548 /* the actual api call to add a textblock */
549
550 #define TB_HEAD() \
551    Evas_Object_Textblock *o; \
552    MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ); \
553    return; \
554    MAGIC_CHECK_END(); \
555    o = (Evas_Object_Textblock *)(obj->object_data); \
556    MAGIC_CHECK(o, Evas_Object_Textblock, MAGIC_OBJ_TEXTBLOCK); \
557    return; \
558    MAGIC_CHECK_END();
559
560 #define TB_HEAD_RETURN(x) \
561    Evas_Object_Textblock *o; \
562    MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ); \
563    return (x); \
564    MAGIC_CHECK_END(); \
565    o = (Evas_Object_Textblock *)(obj->object_data); \
566    MAGIC_CHECK(o, Evas_Object_Textblock, MAGIC_OBJ_TEXTBLOCK); \
567    return (x); \
568    MAGIC_CHECK_END();
569
570
571
572 static Eina_Bool _evas_textblock_cursor_is_at_the_end(const Evas_Textblock_Cursor *cur);
573 static void _evas_textblock_node_text_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *n);
574 static Evas_Object_Textblock_Node_Format *_evas_textblock_cursor_node_format_before_or_at_pos_get(const Evas_Textblock_Cursor *cur);
575 static size_t _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt);
576 static void _evas_textblock_node_format_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n, int visual_adjustment);
577 static void _evas_textblock_node_format_free(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n);
578 static void _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n);
579 static void _evas_textblock_changed(Evas_Object_Textblock *o, Evas_Object *obj);
580 static void _evas_textblock_invalidate_all(Evas_Object_Textblock *o);
581 static void _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Text *n, size_t start, int offset);
582 static void _evas_textblock_cursors_set_node(Evas_Object_Textblock *o, const Evas_Object_Textblock_Node_Text *n, Evas_Object_Textblock_Node_Text *new_node);
583
584 /* styles */
585 /**
586  * @internal
587  * Clears the textblock style passed except for the style_text which is replaced.
588  * @param ts The ts to be cleared. Must not be NULL.
589  * @param style_text the style's text.
590  */
591 static void
592 _style_replace(Evas_Textblock_Style *ts, const char *style_text)
593 {
594    eina_stringshare_replace(&ts->style_text, style_text);
595    if (ts->default_tag) free(ts->default_tag);
596    while (ts->tags)
597      {
598         Evas_Object_Style_Tag *tag;
599
600         tag = (Evas_Object_Style_Tag *)ts->tags;
601         ts->tags = (Evas_Object_Style_Tag *)eina_inlist_remove(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
602         free(tag->tag.tag);
603         free(tag->tag.replace);
604         free(tag);
605      }
606    ts->default_tag = NULL;
607    ts->tags = NULL;
608 }
609
610 /**
611  * @internal
612  * Clears the textblock style passed.
613  * @param ts The ts to be cleared. Must not be NULL.
614  */
615 static void
616 _style_clear(Evas_Textblock_Style *ts)
617 {
618    _style_replace(ts, NULL);
619 }
620
621 /**
622  * @internal
623  * Searches inside the tags stored in the style for the tag matching s.
624  * @param ts The ts to be cleared. Must not be NULL.
625  * @param s The tag to be matched.
626  * @param tag_len the length of the tag string.
627  * @param[out] replace_len The length of the replcaement found. - Must not be NULL.
628  * @return The replacement string found.
629  */
630 static inline const char *
631 _style_match_tag(const Evas_Textblock_Style *ts, const char *s, size_t tag_len, size_t *replace_len)
632 {
633    Evas_Object_Style_Tag *tag;
634
635    /* Try the style tags */
636    EINA_INLIST_FOREACH(ts->tags, tag)
637      {
638         if (tag->tag.tag_len != tag_len) continue;
639         if (!strncmp(tag->tag.tag, s, tag_len))
640           {
641              *replace_len = tag->tag.replace_len;
642              return tag->tag.replace;
643           }
644      }
645
646    /* Try the default tags */
647    {
648       size_t i;
649       const Evas_Object_Style_Tag_Base *btag;
650       for (btag = default_tags, i = 0 ;
651             i < (sizeof(default_tags) / sizeof(default_tags[0])) ;
652             btag++, i++)
653         {
654            if (btag->tag_len != tag_len) continue;
655            if (!strncmp(btag->tag, s, tag_len))
656              {
657                 *replace_len = btag->replace_len;
658                 return btag->replace;
659              }
660         }
661    }
662
663    *replace_len = 0;
664    return NULL;
665 }
666
667 /**
668  * @internal
669  * Clears all the nodes (text and format) of the textblock object.
670  * @param obj The evas object, must not be NULL.
671  */
672 static void
673 _nodes_clear(const Evas_Object *obj)
674 {
675    Evas_Object_Textblock *o;
676
677    o = (Evas_Object_Textblock *)(obj->object_data);
678    while (o->text_nodes)
679      {
680         Evas_Object_Textblock_Node_Text *n;
681
682         n = o->text_nodes;
683         o->text_nodes = _NODE_TEXT(eina_inlist_remove(
684                  EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n)));
685         _evas_textblock_node_text_free(n);
686      }
687    while (o->format_nodes)
688      {
689         Evas_Object_Textblock_Node_Format *n;
690
691         n = o->format_nodes;
692         o->format_nodes = _NODE_FORMAT(eina_inlist_remove(EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n)));
693         _evas_textblock_node_format_free(o, n);
694      }
695 }
696
697 /**
698  * @internal
699  * Unrefs and frees (if needed) a textblock format.
700  * @param obj The Evas_Object, Must not be NULL.
701  * @param fmt the format to be cleaned, must not be NULL.
702  */
703 static void
704 _format_unref_free(const Evas_Object *obj, Evas_Object_Textblock_Format *fmt)
705 {
706    fmt->ref--;
707    if (fmt->ref > 0) return;
708    if (fmt->font.fdesc) evas_font_desc_unref(fmt->font.fdesc);
709    if (fmt->font.source) eina_stringshare_del(fmt->font.source);
710    evas_font_free(obj->layer->evas, fmt->font.font);
711    free(fmt);
712 }
713
714 /**
715  * @internal
716  * Free a layout item
717  * @param obj The evas object, must not be NULL.
718  * @param ln the layout line on which the item is in, must not be NULL.
719  * @param it the layout item to be freed
720  */
721 static void
722 _item_free(const Evas_Object *obj, Evas_Object_Textblock_Line *ln, Evas_Object_Textblock_Item *it)
723 {
724    if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
725      {
726         Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
727
728         evas_common_text_props_content_unref(&ti->text_props);
729      }
730    else
731      {
732         Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
733
734         if (fi->item) eina_stringshare_del(fi->item);
735      }
736    _format_unref_free(obj, it->format);
737    if (ln)
738      {
739         ln->items = (Evas_Object_Textblock_Item *) eina_inlist_remove(
740               EINA_INLIST_GET(ln->items), EINA_INLIST_GET(ln->items));
741      }
742    free(it);
743 }
744
745 /**
746  * @internal
747  * Free a layout line.
748  * @param obj The evas object, must not be NULL.
749  * @param ln the layout line to be freed, must not be NULL.
750  */
751 static void
752 _line_free(Evas_Object_Textblock_Line *ln)
753 {
754    /* Items are freed from the logical list, except for the ellip item */
755    if (ln) free(ln);
756 }
757
758 /* table of html escapes (that i can find) this should be ordered with the
759  * most common first as it's a linear search to match - no hash for this.
760  *
761  * these are stored as one large string and one additional array that
762  * contains the offsets to the tokens for space efficiency.
763  */
764 /**
765  * @internal
766  * @var escape_strings[]
767  * This string consists of NULL terminated pairs of strings, the first of
768  * every pair is an escape and the second is the value of the escape.
769  */
770 static const char escape_strings[] =
771 /* most common escaped stuff */
772 "&quot;\0"     "\x22\0"
773 "&amp;\0"      "\x26\0"
774 "&lt;\0"       "\x3c\0"
775 "&gt;\0"       "\x3e\0"
776 /* all the rest */
777 "&nbsp;\0"     "\xc2\xa0\0"
778 "&iexcl;\0"    "\xc2\xa1\0"
779 "&cent;\0"     "\xc2\xa2\0"
780 "&pound;\0"    "\xc2\xa3\0"
781 "&curren;\0"   "\xc2\xa4\0"
782 "&yen;\0"      "\xc2\xa5\0"
783 "&brvbar;\0"   "\xc2\xa6\0"
784 "&sect;\0"     "\xc2\xa7\0"
785 "&uml;\0"      "\xc2\xa8\0"
786 "&copy;\0"     "\xc2\xa9\0"
787 "&ordf;\0"     "\xc2\xaa\0"
788 "&laquo;\0"    "\xc2\xab\0"
789 "&not;\0"      "\xc2\xac\0"
790 "&reg;\0"      "\xc2\xae\0"
791 "&macr;\0"     "\xc2\xaf\0"
792 "&deg;\0"      "\xc2\xb0\0"
793 "&plusmn;\0"   "\xc2\xb1\0"
794 "&sup2;\0"     "\xc2\xb2\0"
795 "&sup3;\0"     "\xc2\xb3\0"
796 "&acute;\0"    "\xc2\xb4\0"
797 "&micro;\0"    "\xc2\xb5\0"
798 "&para;\0"     "\xc2\xb6\0"
799 "&middot;\0"   "\xc2\xb7\0"
800 "&cedil;\0"    "\xc2\xb8\0"
801 "&sup1;\0"     "\xc2\xb9\0"
802 "&ordm;\0"     "\xc2\xba\0"
803 "&raquo;\0"    "\xc2\xbb\0"
804 "&frac14;\0"   "\xc2\xbc\0"
805 "&frac12;\0"   "\xc2\xbd\0"
806 "&frac34;\0"   "\xc2\xbe\0"
807 "&iquest;\0"   "\xc2\xbf\0"
808 "&Agrave;\0"   "\xc3\x80\0"
809 "&Aacute;\0"   "\xc3\x81\0"
810 "&Acirc;\0"    "\xc3\x82\0"
811 "&Atilde;\0"   "\xc3\x83\0"
812 "&Auml;\0"     "\xc3\x84\0"
813 "&Aring;\0"    "\xc3\x85\0"
814 "&Aelig;\0"    "\xc3\x86\0"
815 "&Ccedil;\0"   "\xc3\x87\0"
816 "&Egrave;\0"   "\xc3\x88\0"
817 "&Eacute;\0"   "\xc3\x89\0"
818 "&Ecirc;\0"    "\xc3\x8a\0"
819 "&Euml;\0"     "\xc3\x8b\0"
820 "&Igrave;\0"   "\xc3\x8c\0"
821 "&Iacute;\0"   "\xc3\x8d\0"
822 "&Icirc;\0"    "\xc3\x8e\0"
823 "&Iuml;\0"     "\xc3\x8f\0"
824 "&Eth;\0"      "\xc3\x90\0"
825 "&Ntilde;\0"   "\xc3\x91\0"
826 "&Ograve;\0"   "\xc3\x92\0"
827 "&Oacute;\0"   "\xc3\x93\0"
828 "&Ocirc;\0"    "\xc3\x94\0"
829 "&Otilde;\0"   "\xc3\x95\0"
830 "&Ouml;\0"     "\xc3\x96\0"
831 "&times;\0"    "\xc3\x97\0"
832 "&Oslash;\0"   "\xc3\x98\0"
833 "&Ugrave;\0"   "\xc3\x99\0"
834 "&Uacute;\0"   "\xc3\x9a\0"
835 "&Ucirc;\0"    "\xc3\x9b\0"
836 "&Yacute;\0"   "\xc3\x9d\0"
837 "&Thorn;\0"    "\xc3\x9e\0"
838 "&szlig;\0"    "\xc3\x9f\0"
839 "&agrave;\0"   "\xc3\xa0\0"
840 "&aacute;\0"   "\xc3\xa1\0"
841 "&acirc;\0"    "\xc3\xa2\0"
842 "&atilde;\0"   "\xc3\xa3\0"
843 "&auml;\0"     "\xc3\xa4\0"
844 "&aring;\0"    "\xc3\xa5\0"
845 "&aelig;\0"    "\xc3\xa6\0"
846 "&ccedil;\0"   "\xc3\xa7\0"
847 "&egrave;\0"   "\xc3\xa8\0"
848 "&eacute;\0"   "\xc3\xa9\0"
849 "&ecirc;\0"    "\xc3\xaa\0"
850 "&euml;\0"     "\xc3\xab\0"
851 "&igrave;\0"   "\xc3\xac\0"
852 "&iacute;\0"   "\xc3\xad\0"
853 "&icirc;\0"    "\xc3\xae\0"
854 "&iuml;\0"     "\xc3\xaf\0"
855 "&eth;\0"      "\xc3\xb0\0"
856 "&ntilde;\0"   "\xc3\xb1\0"
857 "&ograve;\0"   "\xc3\xb2\0"
858 "&oacute;\0"   "\xc3\xb3\0"
859 "&ocirc;\0"    "\xc3\xb4\0"
860 "&otilde;\0"   "\xc3\xb5\0"
861 "&ouml;\0"     "\xc3\xb6\0"
862 "&divide;\0"   "\xc3\xb7\0"
863 "&oslash;\0"   "\xc3\xb8\0"
864 "&ugrave;\0"   "\xc3\xb9\0"
865 "&uacute;\0"   "\xc3\xba\0"
866 "&ucirc;\0"    "\xc3\xbb\0"
867 "&uuml;\0"     "\xc3\xbc\0"
868 "&yacute;\0"   "\xc3\xbd\0"
869 "&thorn;\0"    "\xc3\xbe\0"
870 "&yuml;\0"     "\xc3\xbf\0"
871 "&alpha;\0"    "\xce\x91\0"
872 "&beta;\0"     "\xce\x92\0"
873 "&gamma;\0"    "\xce\x93\0"
874 "&delta;\0"    "\xce\x94\0"
875 "&epsilon;\0"  "\xce\x95\0"
876 "&zeta;\0"     "\xce\x96\0"
877 "&eta;\0"      "\xce\x97\0"
878 "&theta;\0"    "\xce\x98\0"
879 "&iota;\0"     "\xce\x99\0"
880 "&kappa;\0"    "\xce\x9a\0"
881 "&lambda;\0"   "\xce\x9b\0"
882 "&mu;\0"       "\xce\x9c\0"
883 "&nu;\0"       "\xce\x9d\0"
884 "&xi;\0"       "\xce\x9e\0"
885 "&omicron;\0"  "\xce\x9f\0"
886 "&pi;\0"       "\xce\xa0\0"
887 "&rho;\0"      "\xce\xa1\0"
888 "&sigma;\0"    "\xce\xa3\0"
889 "&tau;\0"      "\xce\xa4\0"
890 "&upsilon;\0"  "\xce\xa5\0"
891 "&phi;\0"      "\xce\xa6\0"
892 "&chi;\0"      "\xce\xa7\0"
893 "&psi;\0"      "\xce\xa8\0"
894 "&omega;\0"    "\xce\xa9\0"
895 "&hellip;\0"   "\xe2\x80\xa6\0"
896 "&euro;\0"     "\xe2\x82\xac\0"
897 "&larr;\0"     "\xe2\x86\x90\0"
898 "&uarr;\0"     "\xe2\x86\x91\0"
899 "&rarr;\0"     "\xe2\x86\x92\0"
900 "&darr;\0"     "\xe2\x86\x93\0"
901 "&harr;\0"     "\xe2\x86\x94\0"
902 "&larr;\0"     "\xe2\x87\x90\0"
903 "&rarr;\0"     "\xe2\x87\x92\0"
904 "&forall;\0"   "\xe2\x88\x80\0"
905 "&exist;\0"    "\xe2\x88\x83\0"
906 "&nabla;\0"    "\xe2\x88\x87\0"
907 "&prod;\0"     "\xe2\x88\x8f\0"
908 "&sum;\0"      "\xe2\x88\x91\0"
909 "&and;\0"      "\xe2\x88\xa7\0"
910 "&or;\0"       "\xe2\x88\xa8\0"
911 "&int;\0"      "\xe2\x88\xab\0"
912 "&ne;\0"       "\xe2\x89\xa0\0"
913 "&equiv;\0"    "\xe2\x89\xa1\0"
914 "&oplus;\0"    "\xe2\x8a\x95\0"
915 "&perp;\0"     "\xe2\x8a\xa5\0"
916 "&dagger;\0"   "\xe2\x80\xa0\0"
917 "&Dagger;\0"   "\xe2\x80\xa1\0"
918 "&bull;\0"     "\xe2\x80\xa2\0"
919 ;
920
921 EVAS_MEMPOOL(_mp_obj);
922
923 /**
924  * @internal
925  * Checks if a char is a whitespace.
926  * @param c the unicode codepoint.
927  * @return @c EINA_TRUE if the unicode codepoint is a whitespace, @c EINA_FALSE
928  * otherwise.
929  */
930 static Eina_Bool
931 _is_white(Eina_Unicode c)
932 {
933    /*
934     * unicode list of whitespace chars
935     *
936     * 0009..000D <control-0009>..<control-000D>
937     * 0020 SPACE
938     * 0085 <control-0085>
939     * 00A0 NO-BREAK SPACE
940     * 1680 OGHAM SPACE MARK
941     * 180E MONGOLIAN VOWEL SEPARATOR
942     * 2000..200A EN QUAD..HAIR SPACE
943     * 2028 LINE SEPARATOR
944     * 2029 PARAGRAPH SEPARATOR
945     * 202F NARROW NO-BREAK SPACE
946     * 205F MEDIUM MATHEMATICAL SPACE
947     * 3000 IDEOGRAPHIC SPACE
948     */
949    if (
950          (c == 0x20) ||
951          ((c >= 0x9) && (c <= 0xd)) ||
952          (c == 0x85) ||
953          (c == 0xa0) ||
954          (c == 0x1680) ||
955          (c == 0x180e) ||
956          ((c >= 0x2000) && (c <= 0x200a)) ||
957          (c == 0x2028) ||
958          (c == 0x2029) ||
959          (c == 0x202f) ||
960          (c == 0x205f) ||
961          (c == 0x3000)
962       )
963      return EINA_TRUE;
964    return EINA_FALSE;
965 }
966
967 /**
968  * @internal
969  * Prepends the text between s and p to the main cursor of the object.
970  *
971  * @param cur the cursor to prepend to.
972  * @param[in] s start of the string
973  * @param[in] p end of the string
974  */
975 static void
976 _prepend_text_run(Evas_Textblock_Cursor *cur, const char *s, const char *p)
977 {
978    if ((s) && (p > s))
979      {
980         char *ts;
981
982         ts = alloca(p - s + 1);
983         strncpy(ts, s, p - s);
984         ts[p - s] = 0;
985         evas_textblock_cursor_text_prepend(cur, ts);
986      }
987 }
988
989
990 /**
991  * @internal
992  * Returns the numeric value of HEX chars for example for ch = 'A'
993  * the function will return 10.
994  *
995  * @param ch The HEX char.
996  * @return numeric value of HEX.
997  */
998 static int
999 _hex_string_get(char ch)
1000 {
1001    if ((ch >= '0') && (ch <= '9')) return (ch - '0');
1002    else if ((ch >= 'A') && (ch <= 'F')) return (ch - 'A' + 10);
1003    else if ((ch >= 'a') && (ch <= 'f')) return (ch - 'a' + 10);
1004    return 0;
1005 }
1006
1007 /**
1008  * @internal
1009  * Parses a string of one of the formas:
1010  * 1. "#RRGGBB"
1011  * 2. "#RRGGBBAA"
1012  * 3. "#RGB"
1013  * 4. "#RGBA"
1014  * To the rgba values.
1015  *
1016  * @param[in] str The string to parse - NOT NULL.
1017  * @param[out] r The Red value - NOT NULL.
1018  * @param[out] g The Green value - NOT NULL.
1019  * @param[out] b The Blue value - NOT NULL.
1020  * @param[out] a The Alpha value - NOT NULL.
1021  */
1022 static void
1023 _format_color_parse(const char *str, unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a)
1024 {
1025    int slen;
1026
1027    slen = strlen(str);
1028    *r = *g = *b = *a = 0;
1029
1030    if (slen == 7) /* #RRGGBB */
1031      {
1032         *r = (_hex_string_get(str[1]) << 4) | (_hex_string_get(str[2]));
1033         *g = (_hex_string_get(str[3]) << 4) | (_hex_string_get(str[4]));
1034         *b = (_hex_string_get(str[5]) << 4) | (_hex_string_get(str[6]));
1035         *a = 0xff;
1036      }
1037    else if (slen == 9) /* #RRGGBBAA */
1038      {
1039         *r = (_hex_string_get(str[1]) << 4) | (_hex_string_get(str[2]));
1040         *g = (_hex_string_get(str[3]) << 4) | (_hex_string_get(str[4]));
1041         *b = (_hex_string_get(str[5]) << 4) | (_hex_string_get(str[6]));
1042         *a = (_hex_string_get(str[7]) << 4) | (_hex_string_get(str[8]));
1043      }
1044    else if (slen == 4) /* #RGB */
1045      {
1046         *r = _hex_string_get(str[1]);
1047         *r = (*r << 4) | *r;
1048         *g = _hex_string_get(str[2]);
1049         *g = (*g << 4) | *g;
1050         *b = _hex_string_get(str[3]);
1051         *b = (*b << 4) | *b;
1052         *a = 0xff;
1053      }
1054    else if (slen == 5) /* #RGBA */
1055      {
1056         *r = _hex_string_get(str[1]);
1057         *r = (*r << 4) | *r;
1058         *g = _hex_string_get(str[2]);
1059         *g = (*g << 4) | *g;
1060         *b = _hex_string_get(str[3]);
1061         *b = (*b << 4) | *b;
1062         *a = _hex_string_get(str[4]);
1063         *a = (*a << 4) | *a;
1064      }
1065    *r = (*r * *a) / 255;
1066    *g = (*g * *a) / 255;
1067    *b = (*b * *a) / 255;
1068 }
1069
1070 /* The refcount for the formats. */
1071 static int format_refcount = 0;
1072 /* Holders for the stringshares */
1073 static const char *fontstr = NULL;
1074 static const char *font_fallbacksstr = NULL;
1075 static const char *font_sizestr = NULL;
1076 static const char *font_sourcestr = NULL;
1077 static const char *font_weightstr = NULL;
1078 static const char *font_stylestr = NULL;
1079 static const char *font_widthstr = NULL;
1080 static const char *langstr = NULL;
1081 static const char *colorstr = NULL;
1082 static const char *underline_colorstr = NULL;
1083 static const char *underline2_colorstr = NULL;
1084 static const char *underline_dash_colorstr = NULL;
1085 static const char *outline_colorstr = NULL;
1086 static const char *shadow_colorstr = NULL;
1087 static const char *glow_colorstr = NULL;
1088 static const char *glow2_colorstr = NULL;
1089 static const char *backing_colorstr = NULL;
1090 static const char *strikethrough_colorstr = NULL;
1091 static const char *alignstr = NULL;
1092 static const char *valignstr = NULL;
1093 static const char *wrapstr = NULL;
1094 static const char *left_marginstr = NULL;
1095 static const char *right_marginstr = NULL;
1096 static const char *underlinestr = NULL;
1097 static const char *strikethroughstr = NULL;
1098 static const char *backingstr = NULL;
1099 static const char *stylestr = NULL;
1100 static const char *tabstopsstr = NULL;
1101 static const char *linesizestr = NULL;
1102 static const char *linerelsizestr = NULL;
1103 static const char *linegapstr = NULL;
1104 static const char *linerelgapstr = NULL;
1105 static const char *itemstr = NULL;
1106 static const char *linefillstr = NULL;
1107 static const char *ellipsisstr = NULL;
1108 static const char *passwordstr = NULL;
1109 static const char *underline_dash_widthstr = NULL;
1110 static const char *underline_dash_gapstr = NULL;
1111
1112 /**
1113  * @internal
1114  * Init the format strings.
1115  */
1116 static void
1117 _format_command_init(void)
1118 {
1119    if (format_refcount == 0)
1120      {
1121         fontstr = eina_stringshare_add("font");
1122         font_fallbacksstr = eina_stringshare_add("font_fallbacks");
1123         font_sizestr = eina_stringshare_add("font_size");
1124         font_sourcestr = eina_stringshare_add("font_source");
1125         font_weightstr = eina_stringshare_add("font_weight");
1126         font_stylestr = eina_stringshare_add("font_style");
1127         font_widthstr = eina_stringshare_add("font_width");
1128         langstr = eina_stringshare_add("lang");
1129         colorstr = eina_stringshare_add("color");
1130         underline_colorstr = eina_stringshare_add("underline_color");
1131         underline2_colorstr = eina_stringshare_add("underline2_color");
1132         underline_dash_colorstr = eina_stringshare_add("underline_dash_color");
1133         outline_colorstr = eina_stringshare_add("outline_color");
1134         shadow_colorstr = eina_stringshare_add("shadow_color");
1135         glow_colorstr = eina_stringshare_add("glow_color");
1136         glow2_colorstr = eina_stringshare_add("glow2_color");
1137         backing_colorstr = eina_stringshare_add("backing_color");
1138         strikethrough_colorstr = eina_stringshare_add("strikethrough_color");
1139         alignstr = eina_stringshare_add("align");
1140         valignstr = eina_stringshare_add("valign");
1141         wrapstr = eina_stringshare_add("wrap");
1142         left_marginstr = eina_stringshare_add("left_margin");
1143         right_marginstr = eina_stringshare_add("right_margin");
1144         underlinestr = eina_stringshare_add("underline");
1145         strikethroughstr = eina_stringshare_add("strikethrough");
1146         backingstr = eina_stringshare_add("backing");
1147         stylestr = eina_stringshare_add("style");
1148         tabstopsstr = eina_stringshare_add("tabstops");
1149         linesizestr = eina_stringshare_add("linesize");
1150         linerelsizestr = eina_stringshare_add("linerelsize");
1151         linegapstr = eina_stringshare_add("linegap");
1152         linerelgapstr = eina_stringshare_add("linerelgap");
1153         itemstr = eina_stringshare_add("item");
1154         linefillstr = eina_stringshare_add("linefill");
1155         ellipsisstr = eina_stringshare_add("ellipsis");
1156         passwordstr = eina_stringshare_add("password");
1157         underline_dash_widthstr = eina_stringshare_add("underline_dash_width");
1158         underline_dash_gapstr = eina_stringshare_add("underline_dash_gap");
1159      }
1160    format_refcount++;
1161 }
1162
1163 /**
1164  * @internal
1165  * Shutdown the format strings.
1166  */
1167 static void
1168 _format_command_shutdown(void)
1169 {
1170    if (--format_refcount > 0) return;
1171
1172    eina_stringshare_del(fontstr);
1173    eina_stringshare_del(font_fallbacksstr);
1174    eina_stringshare_del(font_sizestr);
1175    eina_stringshare_del(font_sourcestr);
1176    eina_stringshare_del(font_weightstr);
1177    eina_stringshare_del(font_stylestr);
1178    eina_stringshare_del(font_widthstr);
1179    eina_stringshare_del(langstr);
1180    eina_stringshare_del(colorstr);
1181    eina_stringshare_del(underline_colorstr);
1182    eina_stringshare_del(underline2_colorstr);
1183    eina_stringshare_del(underline_dash_colorstr);
1184    eina_stringshare_del(outline_colorstr);
1185    eina_stringshare_del(shadow_colorstr);
1186    eina_stringshare_del(glow_colorstr);
1187    eina_stringshare_del(glow2_colorstr);
1188    eina_stringshare_del(backing_colorstr);
1189    eina_stringshare_del(strikethrough_colorstr);
1190    eina_stringshare_del(alignstr);
1191    eina_stringshare_del(valignstr);
1192    eina_stringshare_del(wrapstr);
1193    eina_stringshare_del(left_marginstr);
1194    eina_stringshare_del(right_marginstr);
1195    eina_stringshare_del(underlinestr);
1196    eina_stringshare_del(strikethroughstr);
1197    eina_stringshare_del(backingstr);
1198    eina_stringshare_del(stylestr);
1199    eina_stringshare_del(tabstopsstr);
1200    eina_stringshare_del(linesizestr);
1201    eina_stringshare_del(linerelsizestr);
1202    eina_stringshare_del(linegapstr);
1203    eina_stringshare_del(linerelgapstr);
1204    eina_stringshare_del(itemstr);
1205    eina_stringshare_del(linefillstr);
1206    eina_stringshare_del(ellipsisstr);
1207    eina_stringshare_del(passwordstr);
1208    eina_stringshare_del(underline_dash_widthstr);
1209    eina_stringshare_del(underline_dash_gapstr);
1210 }
1211
1212 /**
1213  * @internal
1214  * Copies str to dst while removing the \\ char, i.e unescape the escape sequences.
1215  *
1216  * @param[out] dst the destination string - Should not be NULL.
1217  * @param[in] src the source string - Should not be NULL.
1218  */
1219 static void
1220 _format_clean_param(char *dst, const char *src)
1221 {
1222    const char *ss;
1223    char *ds;
1224
1225    ds = dst;
1226    for (ss = src; *ss; ss++, ds++)
1227      {
1228         if ((*ss == '\\') && *(ss + 1)) ss++;
1229         *ds = *ss;
1230      }
1231    *ds = 0;
1232 }
1233
1234 /**
1235  * @internal
1236  * Parses the cmd and parameter and adds the parsed format to fmt.
1237  *
1238  * @param obj the evas object - should not be NULL.
1239  * @param fmt The format to populate - should not be NULL.
1240  * @param[in] cmd the command to process, should be stringshared.
1241  * @param[in] param the parameter of the command.
1242  */
1243 static void
1244 _format_command(Evas_Object *obj, Evas_Object_Textblock_Format *fmt, const char *cmd, const char *param)
1245 {
1246    int len;
1247    char *tmp_param;
1248
1249    len = strlen(param);
1250    tmp_param = alloca(len + 1);
1251
1252    _format_clean_param(tmp_param, param);
1253
1254    /* If we are changing the font, create the fdesc. */
1255    if ((cmd == font_weightstr) || (cmd == font_widthstr) ||
1256          (cmd == font_stylestr) || (cmd == langstr) ||
1257          (cmd == fontstr) || (cmd == font_fallbacksstr))
1258      {
1259         if (!fmt->font.fdesc)
1260           {
1261              fmt->font.fdesc = evas_font_desc_new();
1262           }
1263         else if (!fmt->font.fdesc->is_new)
1264           {
1265              Evas_Font_Description *old = fmt->font.fdesc;
1266              fmt->font.fdesc = evas_font_desc_dup(fmt->font.fdesc);
1267              if (old) evas_font_desc_unref(old);
1268           }
1269      }
1270
1271
1272    if (cmd == fontstr)
1273      {
1274         evas_font_name_parse(fmt->font.fdesc, tmp_param);
1275      }
1276    else if (cmd == font_fallbacksstr)
1277      {
1278         eina_stringshare_replace(&(fmt->font.fdesc->fallbacks), tmp_param);
1279      }
1280    else if (cmd == font_sizestr)
1281      {
1282         int v;
1283
1284         v = atoi(tmp_param);
1285         if (v != fmt->font.size)
1286           {
1287              fmt->font.size = v;
1288           }
1289      }
1290    else if (cmd == font_sourcestr)
1291      {
1292         if ((!fmt->font.source) ||
1293               ((fmt->font.source) && (strcmp(fmt->font.source, tmp_param))))
1294           {
1295              if (fmt->font.source) eina_stringshare_del(fmt->font.source);
1296              fmt->font.source = eina_stringshare_add(tmp_param);
1297           }
1298      }
1299    else if (cmd == font_weightstr)
1300      {
1301         fmt->font.fdesc->weight = evas_font_style_find(tmp_param,
1302               tmp_param + strlen(tmp_param), EVAS_FONT_STYLE_WEIGHT);
1303      }
1304    else if (cmd == font_stylestr)
1305      {
1306         fmt->font.fdesc->slant = evas_font_style_find(tmp_param,
1307               tmp_param + strlen(tmp_param), EVAS_FONT_STYLE_SLANT);
1308      }
1309    else if (cmd == font_widthstr)
1310      {
1311         fmt->font.fdesc->width = evas_font_style_find(tmp_param,
1312               tmp_param + strlen(tmp_param), EVAS_FONT_STYLE_WIDTH);
1313      }
1314    else if (cmd == langstr)
1315      {
1316         eina_stringshare_replace(&(fmt->font.fdesc->lang), tmp_param);
1317      }
1318    else if (cmd == colorstr)
1319      _format_color_parse(tmp_param,
1320            &(fmt->color.normal.r), &(fmt->color.normal.g),
1321            &(fmt->color.normal.b), &(fmt->color.normal.a));
1322    else if (cmd == underline_colorstr)
1323      _format_color_parse(tmp_param,
1324            &(fmt->color.underline.r), &(fmt->color.underline.g),
1325            &(fmt->color.underline.b), &(fmt->color.underline.a));
1326    else if (cmd == underline2_colorstr)
1327      _format_color_parse(tmp_param,
1328            &(fmt->color.underline2.r), &(fmt->color.underline2.g),
1329            &(fmt->color.underline2.b), &(fmt->color.underline2.a));
1330    else if (cmd == underline_dash_colorstr)
1331      _format_color_parse(tmp_param,
1332            &(fmt->color.underline_dash.r), &(fmt->color.underline_dash.g),
1333            &(fmt->color.underline_dash.b), &(fmt->color.underline_dash.a));
1334    else if (cmd == outline_colorstr)
1335      _format_color_parse(tmp_param,
1336            &(fmt->color.outline.r), &(fmt->color.outline.g),
1337            &(fmt->color.outline.b), &(fmt->color.outline.a));
1338    else if (cmd == shadow_colorstr)
1339      _format_color_parse(tmp_param,
1340            &(fmt->color.shadow.r), &(fmt->color.shadow.g),
1341            &(fmt->color.shadow.b), &(fmt->color.shadow.a));
1342    else if (cmd == glow_colorstr)
1343      _format_color_parse(tmp_param,
1344            &(fmt->color.glow.r), &(fmt->color.glow.g),
1345            &(fmt->color.glow.b), &(fmt->color.glow.a));
1346    else if (cmd == glow2_colorstr)
1347      _format_color_parse(tmp_param,
1348            &(fmt->color.glow2.r), &(fmt->color.glow2.g),
1349            &(fmt->color.glow2.b), &(fmt->color.glow2.a));
1350    else if (cmd == backing_colorstr)
1351      _format_color_parse(tmp_param,
1352            &(fmt->color.backing.r), &(fmt->color.backing.g),
1353            &(fmt->color.backing.b), &(fmt->color.backing.a));
1354    else if (cmd == strikethrough_colorstr)
1355      _format_color_parse(tmp_param,
1356            &(fmt->color.strikethrough.r), &(fmt->color.strikethrough.g),
1357            &(fmt->color.strikethrough.b), &(fmt->color.strikethrough.a));
1358    else if (cmd == alignstr)
1359      {
1360         if (!strcmp(tmp_param, "auto"))
1361           {
1362              fmt->halign_auto = EINA_TRUE;
1363           }
1364         else
1365           {
1366              if (!strcmp(tmp_param, "middle")) fmt->halign = 0.5;
1367              else if (!strcmp(tmp_param, "center")) fmt->halign = 0.5;
1368              else if (!strcmp(tmp_param, "left")) fmt->halign = 0.0;
1369              else if (!strcmp(tmp_param, "right")) fmt->halign = 1.0;
1370              else
1371                {
1372                   char *endptr = NULL;
1373                   double val = strtod(tmp_param, &endptr);
1374                   if (endptr)
1375                     {
1376                        while (*endptr && _is_white(*endptr))
1377                          endptr++;
1378                        if (*endptr == '%')
1379                          val /= 100.0;
1380                     }
1381                   fmt->halign = val;
1382                   if (fmt->halign < 0.0) fmt->halign = 0.0;
1383                   else if (fmt->halign > 1.0) fmt->halign = 1.0;
1384                }
1385              fmt->halign_auto = EINA_FALSE;
1386           }
1387      }
1388    else if (cmd == valignstr)
1389      {
1390         if (!strcmp(tmp_param, "top")) fmt->valign = 0.0;
1391         else if (!strcmp(tmp_param, "middle")) fmt->valign = 0.5;
1392         else if (!strcmp(tmp_param, "center")) fmt->valign = 0.5;
1393         else if (!strcmp(tmp_param, "bottom")) fmt->valign = 1.0;
1394         else if (!strcmp(tmp_param, "baseline")) fmt->valign = -1.0;
1395         else if (!strcmp(tmp_param, "base")) fmt->valign = -1.0;
1396         else
1397           {
1398              char *endptr = NULL;
1399              double val = strtod(tmp_param, &endptr);
1400              if (endptr)
1401                {
1402                   while (*endptr && _is_white(*endptr))
1403                     endptr++;
1404                   if (*endptr == '%')
1405                     val /= 100.0;
1406                }
1407              fmt->valign = val;
1408              if (fmt->valign < 0.0) fmt->valign = 0.0;
1409              else if (fmt->valign > 1.0) fmt->valign = 1.0;
1410           }
1411      }
1412    else if (cmd == wrapstr)
1413      {
1414         if (!strcmp(tmp_param, "word"))
1415           {
1416              fmt->wrap_word = 1;
1417              fmt->wrap_char = fmt->wrap_mixed = 0;
1418           }
1419         else if (!strcmp(tmp_param, "char"))
1420           {
1421              fmt->wrap_word = fmt->wrap_mixed = 0;
1422              fmt->wrap_char = 1;
1423           }
1424         else if (!strcmp(tmp_param, "mixed"))
1425           {
1426              fmt->wrap_word = fmt->wrap_char = 0;
1427              fmt->wrap_mixed = 1;
1428           }
1429         else
1430           {
1431              fmt->wrap_word = fmt->wrap_mixed = fmt->wrap_char = 0;
1432           }
1433      }
1434    else if (cmd == left_marginstr)
1435      {
1436         if (!strcmp(tmp_param, "reset"))
1437           fmt->margin.l = 0;
1438         else
1439           {
1440              if (tmp_param[0] == '+')
1441                fmt->margin.l += atoi(&(tmp_param[1]));
1442              else if (tmp_param[0] == '-')
1443                fmt->margin.l -= atoi(&(tmp_param[1]));
1444              else
1445                fmt->margin.l = atoi(tmp_param);
1446              if (fmt->margin.l < 0) fmt->margin.l = 0;
1447           }
1448      }
1449    else if (cmd == right_marginstr)
1450      {
1451         if (!strcmp(tmp_param, "reset"))
1452           fmt->margin.r = 0;
1453         else
1454           {
1455              if (tmp_param[0] == '+')
1456                fmt->margin.r += atoi(&(tmp_param[1]));
1457              else if (tmp_param[0] == '-')
1458                fmt->margin.r -= atoi(&(tmp_param[1]));
1459              else
1460                fmt->margin.r = atoi(tmp_param);
1461              if (fmt->margin.r < 0) fmt->margin.r = 0;
1462           }
1463      }
1464    else if (cmd == underlinestr)
1465      {
1466         if (!strcmp(tmp_param, "off"))
1467           {
1468              fmt->underline = 0;
1469              fmt->underline2 = 0;
1470           }
1471         else if ((!strcmp(tmp_param, "on")) ||
1472               (!strcmp(tmp_param, "single")))
1473           {
1474              fmt->underline = 1;
1475              fmt->underline2 = 0;
1476           }
1477         else if (!strcmp(tmp_param, "double"))
1478           {
1479              fmt->underline = 1;
1480              fmt->underline2 = 1;
1481           }
1482         else if (!strcmp(tmp_param, "dashed"))
1483           fmt->underline_dash = 1;
1484      }
1485    else if (cmd == strikethroughstr)
1486      {
1487         if (!strcmp(tmp_param, "off"))
1488           fmt->strikethrough = 0;
1489         else if (!strcmp(tmp_param, "on"))
1490           fmt->strikethrough = 1;
1491      }
1492    else if (cmd == backingstr)
1493      {
1494         if (!strcmp(tmp_param, "off"))
1495           fmt->backing = 0;
1496         else if (!strcmp(tmp_param, "on"))
1497           fmt->backing = 1;
1498      }
1499    else if (cmd == stylestr)
1500      {
1501         char *p1, *p2, *p, *pp;
1502
1503         p1 = alloca(len + 1);
1504         *p1 = 0;
1505         p2 = alloca(len + 1);
1506         *p2 = 0;
1507         /* no comma */
1508         if (!strstr(tmp_param, ",")) p1 = tmp_param;
1509         else
1510           {
1511              /* split string "str1,str2" into p1 and p2 (if we have more than
1512               * 1 str2 eg "str1,str2,str3,str4" then we don't care. p2 just
1513               * ends up being the last one as right now it's only valid to have
1514               * 1 comma and 2 strings */
1515              pp = p1;
1516              for (p = tmp_param; *p; p++)
1517                {
1518                   if (*p == ',')
1519                     {
1520                        *pp = 0;
1521                        pp = p2;
1522                        continue;
1523                     }
1524                   *pp = *p;
1525                   pp++;
1526                }
1527              *pp = 0;
1528           }
1529         if      (!strcmp(p1, "off"))                 fmt->style = EVAS_TEXT_STYLE_PLAIN;
1530         else if (!strcmp(p1, "none"))                fmt->style = EVAS_TEXT_STYLE_PLAIN;
1531         else if (!strcmp(p1, "plain"))               fmt->style = EVAS_TEXT_STYLE_PLAIN;
1532         else if (!strcmp(p1, "shadow"))              fmt->style = EVAS_TEXT_STYLE_SHADOW;
1533         else if (!strcmp(p1, "outline"))             fmt->style = EVAS_TEXT_STYLE_OUTLINE;
1534         else if (!strcmp(p1, "soft_outline"))        fmt->style = EVAS_TEXT_STYLE_SOFT_OUTLINE;
1535         else if (!strcmp(p1, "outline_shadow"))      fmt->style = EVAS_TEXT_STYLE_OUTLINE_SHADOW;
1536         else if (!strcmp(p1, "outline_soft_shadow")) fmt->style = EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW;
1537         else if (!strcmp(p1, "glow"))                fmt->style = EVAS_TEXT_STYLE_GLOW;
1538         else if (!strcmp(p1, "far_shadow"))          fmt->style = EVAS_TEXT_STYLE_FAR_SHADOW;
1539         else if (!strcmp(p1, "soft_shadow"))         fmt->style = EVAS_TEXT_STYLE_SOFT_SHADOW;
1540         else if (!strcmp(p1, "far_soft_shadow"))     fmt->style = EVAS_TEXT_STYLE_FAR_SOFT_SHADOW;
1541         else                                         fmt->style = EVAS_TEXT_STYLE_PLAIN;
1542         
1543         if (*p2)
1544           {
1545              if      (!strcmp(p2, "bottom_right")) EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT);
1546              else if (!strcmp(p2, "bottom"))       EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM);
1547              else if (!strcmp(p2, "bottom_left"))  EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT);
1548              else if (!strcmp(p2, "left"))         EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT);
1549              else if (!strcmp(p2, "top_left"))     EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT);
1550              else if (!strcmp(p2, "top"))          EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP);
1551              else if (!strcmp(p2, "top_right"))    EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT);
1552              else if (!strcmp(p2, "right"))        EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT);
1553              else                                  EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT);
1554           }
1555      }
1556    else if (cmd == tabstopsstr)
1557      {
1558         fmt->tabstops = atoi(tmp_param);
1559         if (fmt->tabstops < 1) fmt->tabstops = 1;
1560      }
1561    else if (cmd == linesizestr)
1562      {
1563         fmt->linesize = atoi(tmp_param);
1564         fmt->linerelsize = 0.0;
1565      }
1566    else if (cmd == linerelsizestr)
1567      {
1568         char *endptr = NULL;
1569         double val = strtod(tmp_param, &endptr);
1570         if (endptr)
1571           {
1572              while (*endptr && _is_white(*endptr))
1573                endptr++;
1574              if (*endptr == '%')
1575                {
1576                   fmt->linerelsize = val / 100.0;
1577                   fmt->linesize = 0;
1578                   if (fmt->linerelsize < 0.0) fmt->linerelsize = 0.0;
1579                }
1580           }
1581      }
1582    else if (cmd == linegapstr)
1583      {
1584         fmt->linegap = atoi(tmp_param);
1585         fmt->linerelgap = 0.0;
1586      }
1587    else if (cmd == linerelgapstr)
1588      {
1589         char *endptr = NULL;
1590         double val = strtod(tmp_param, &endptr);
1591         if (endptr)
1592           {
1593              while (*endptr && _is_white(*endptr))
1594                endptr++;
1595              if (*endptr == '%')
1596                {
1597                   fmt->linerelgap = val / 100.0;
1598                   fmt->linegap = 0;
1599                   if (fmt->linerelgap < 0.0) fmt->linerelgap = 0.0;
1600                }
1601           }
1602      }
1603    else if (cmd == itemstr)
1604      {
1605         // itemstr == replacement object items in textblock - inline imges
1606         // for example
1607      }
1608    else if (cmd == linefillstr)
1609      {
1610         char *endptr = NULL;
1611         double val = strtod(tmp_param, &endptr);
1612         if (endptr)
1613           {
1614              while (*endptr && _is_white(*endptr))
1615                endptr++;
1616              if (*endptr == '%')
1617                {
1618                   fmt->linefill = val / 100.0;
1619                   if (fmt->linefill < 0.0) fmt->linefill = 0.0;
1620                }
1621           }
1622      }
1623    else if (cmd == ellipsisstr)
1624      {
1625         char *endptr = NULL;
1626         fmt->ellipsis = strtod(tmp_param, &endptr);
1627         if ((fmt->ellipsis < 0.0) || (fmt->ellipsis > 1.0))
1628           fmt->ellipsis = -1.0;
1629         else
1630           {
1631              Evas_Object_Textblock *o;
1632              
1633              o = (Evas_Object_Textblock *)(obj->object_data);
1634              o->have_ellipsis = 1;
1635           }
1636      }
1637    else if (cmd == passwordstr)
1638      {
1639         if (!strcmp(tmp_param, "off"))
1640           fmt->password = 0;
1641         else if (!strcmp(tmp_param, "on"))
1642           fmt->password = 1;
1643      }
1644    else if (cmd == underline_dash_widthstr)
1645      {
1646         fmt->underline_dash_width = atoi(tmp_param);
1647         if (fmt->underline_dash_width <= 0) fmt->underline_dash_width = 1;
1648      }
1649    else if (cmd == underline_dash_gapstr)
1650      {
1651         fmt->underline_dash_gap = atoi(tmp_param);
1652         if (fmt->underline_dash_gap <= 0) fmt->underline_dash_gap = 1;
1653      }
1654 }
1655
1656 /**
1657  * @internal
1658  * Returns @c EINA_TRUE if the item is a format parameter, @c EINA_FALSE
1659  * otherwise.
1660  *
1661  * @param[in] item the item to check - Not NULL.
1662  */
1663 static Eina_Bool
1664 _format_is_param(const char *item)
1665 {
1666    if (strchr(item, '=')) return EINA_TRUE;
1667    return EINA_FALSE;
1668 }
1669
1670 /**
1671  * @internal
1672  * Parse the format item and populate key and val with the stringshares that
1673  * corrospond to the formats parsed.
1674  * It expects item to be of the structure:
1675  * "key=val"
1676  *
1677  * @param[in] item the item to parse - Not NULL.
1678  * @param[out] key where to store the key at - Not NULL.
1679  * @param[out] val where to store the value at - Not NULL.
1680  */
1681 static void
1682 _format_param_parse(const char *item, const char **key, const char **val)
1683 {
1684    const char *start, *end, *quote;
1685
1686    start = strchr(item, '=');
1687    *key = eina_stringshare_add_length(item, start - item);
1688    start++; /* Advance after the '=' */
1689    /* If we can find a quote, our new delimiter is a quote, not a space. */
1690    if ((quote = strchr(start, '\'')))
1691      {
1692         start = quote + 1;
1693         end = strchr(start, '\'');
1694      }
1695    else
1696      {
1697         end = strchr(start, ' ');
1698      }
1699
1700    /* Null terminate before the spaces */
1701    if (end)
1702      {
1703         *val = eina_stringshare_add_length(start, end - start);
1704      }
1705    else
1706      {
1707         *val = eina_stringshare_add(start);
1708      }
1709 }
1710
1711 /**
1712  * @internal
1713  * This function parses the format passed in *s and advances s to point to the
1714  * next format item, while returning the current one as the return value.
1715  * @param s The current and returned position in the format string.
1716  * @return the current item parsed from the string.
1717  */
1718 static const char *
1719 _format_parse(const char **s)
1720 {
1721    const char *p;
1722    const char *s1 = NULL, *s2 = NULL;
1723    Eina_Bool quote = EINA_FALSE;;
1724
1725    p = *s;
1726    if (*p == 0) return NULL;
1727    for (;;)
1728      {
1729         if (!s1)
1730           {
1731              if (*p != ' ') s1 = p;
1732              if (*p == 0) break;
1733           }
1734         else if (!s2)
1735           {
1736              if (*p == '\'')
1737                {
1738                   quote = !quote;
1739                }
1740
1741              if ((p > *s) && (p[-1] != '\\') && (!quote))
1742                {
1743                   if (*p == ' ') s2 = p;
1744                }
1745              if (*p == 0) s2 = p;
1746           }
1747         p++;
1748         if (s1 && s2)
1749           {
1750              *s = s2;
1751              return s1;
1752           }
1753      }
1754    *s = p;
1755    return NULL;
1756 }
1757
1758 /**
1759  * @internal
1760  * Parse the format str and populate fmt with the formats found.
1761  *
1762  * @param obj The evas object - Not NULL.
1763  * @param[out] fmt The format to populate - Not NULL.
1764  * @param[in] str the string to parse.- Not NULL.
1765  */
1766 static void
1767 _format_fill(Evas_Object *obj, Evas_Object_Textblock_Format *fmt, const char *str)
1768 {
1769    const char *s;
1770    const char *item;
1771
1772    s = str;
1773
1774    /* get rid of any spaces at the start of the string */
1775    while (*s == ' ') s++;
1776
1777    while ((item = _format_parse(&s)))
1778      {
1779         if (_format_is_param(item))
1780           {
1781              const char *key = NULL, *val = NULL;
1782
1783              _format_param_parse(item, &key, &val);
1784              _format_command(obj, fmt, key, val);
1785              eina_stringshare_del(key);
1786              eina_stringshare_del(val);
1787           }
1788         else
1789           {
1790              /* immediate - not handled here */
1791           }
1792      }
1793 }
1794
1795 /**
1796  * @internal
1797  * Duplicate a format and return the duplicate.
1798  *
1799  * @param obj The evas object - Not NULL.
1800  * @param[in] fmt The format to duplicate - Not NULL.
1801  * @return the copy of the format.
1802  */
1803 static Evas_Object_Textblock_Format *
1804 _format_dup(Evas_Object *obj, const Evas_Object_Textblock_Format *fmt)
1805 {
1806    Evas_Object_Textblock_Format *fmt2;
1807
1808    fmt2 = calloc(1, sizeof(Evas_Object_Textblock_Format));
1809    memcpy(fmt2, fmt, sizeof(Evas_Object_Textblock_Format));
1810    fmt2->ref = 1;
1811    fmt2->font.fdesc = evas_font_desc_ref(fmt->font.fdesc);
1812
1813    if (fmt->font.source) fmt2->font.source = eina_stringshare_add(fmt->font.source);
1814
1815    /* FIXME: just ref the font here... */
1816    fmt2->font.font = evas_font_load(obj->layer->evas, fmt2->font.fdesc,
1817          fmt2->font.source, (int)(((double) fmt2->font.size) * obj->cur.scale));
1818    return fmt2;
1819 }
1820
1821
1822
1823
1824 /**
1825  * @internal
1826  * @typedef Ctxt
1827  *
1828  * A pack of information that needed to be passed around in the layout engine,
1829  * packed for easier access.
1830  */
1831 typedef struct _Ctxt Ctxt;
1832
1833 struct _Ctxt
1834 {
1835    Evas_Object *obj;
1836    Evas_Object_Textblock *o;
1837
1838    Evas_Object_Textblock_Paragraph *paragraphs;
1839    Evas_Object_Textblock_Paragraph *par;
1840    Evas_Object_Textblock_Line *ln;
1841
1842
1843    Eina_List *format_stack;
1844    Evas_Object_Textblock_Format *fmt;
1845
1846    int x, y;
1847    int w, h;
1848    int wmax, hmax;
1849    int maxascent, maxdescent;
1850    int marginl, marginr;
1851    int line_no;
1852    int underline_extend;
1853    int have_underline, have_underline2;
1854    double align, valign;
1855    Eina_Bool align_auto : 1;
1856    Eina_Bool width_changed : 1;
1857 };
1858
1859 static void _layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Text_Item *ti, Eina_List *rel);
1860 static void _text_item_update_sizes(Ctxt *c, Evas_Object_Textblock_Text_Item *ti);
1861 static void _layout_do_format(const Evas_Object *obj, Ctxt *c, Evas_Object_Textblock_Format **_fmt, Evas_Object_Textblock_Node_Format *n, int *style_pad_l, int *style_pad_r, int *style_pad_t, int *style_pad_b, Eina_Bool create_item);
1862 /**
1863  * @internal
1864  * Adjust the ascent/descent of the format and context.
1865  *
1866  * @param maxascent The ascent to update - Not NUL.
1867  * @param maxdescent The descent to update - Not NUL.
1868  * @param fmt The format to adjust - NOT NULL.
1869  */
1870 static void
1871 _layout_format_ascent_descent_adjust(const Evas_Object *obj,
1872       Evas_Coord *maxascent, Evas_Coord *maxdescent,
1873       Evas_Object_Textblock_Format *fmt)
1874 {
1875    int ascent, descent;
1876
1877    if (fmt->font.font)
1878      {
1879         //      ascent = c->ENFN->font_max_ascent_get(c->ENDT, fmt->font.font);
1880         //      descent = c->ENFN->font_max_descent_get(c->ENDT, fmt->font.font);
1881         ascent = ENFN->font_ascent_get(ENDT, fmt->font.font);
1882         descent = ENFN->font_descent_get(ENDT, fmt->font.font);
1883         if (fmt->linesize > 0)
1884           {
1885              if ((ascent + descent) < fmt->linesize)
1886                {
1887                   ascent = ((fmt->linesize * ascent) / (ascent + descent));
1888                   descent = fmt->linesize - ascent;
1889                }
1890           }
1891         else if (fmt->linerelsize > 0.0)
1892           {
1893              descent = descent * fmt->linerelsize;
1894              ascent = ascent * fmt->linerelsize;
1895           }
1896         descent += fmt->linegap;
1897         descent += ((ascent + descent) * fmt->linerelgap);
1898         if (*maxascent < ascent) *maxascent = ascent;
1899         if (*maxdescent < descent) *maxdescent = descent;
1900         if (fmt->linefill > 0.0)
1901           {
1902              int dh;
1903
1904              dh = obj->cur.geometry.h - (*maxascent + *maxdescent);
1905              if (dh < 0) dh = 0;
1906              dh = fmt->linefill * dh;
1907              *maxdescent += dh / 2;
1908              *maxascent += dh - (dh / 2);
1909              // FIXME: set flag that says "if heigh changes - reformat"
1910           }
1911      }
1912 }
1913
1914 /**
1915  * @internal
1916  * Create a new line using the info from the format and update the format
1917  * and context.
1918  *
1919  * @param c The context to work on - Not NULL.
1920  * @param fmt The format to use info from - NOT NULL.
1921  */
1922 static void
1923 _layout_line_new(Ctxt *c, Evas_Object_Textblock_Format *fmt)
1924 {
1925    c->ln = calloc(1, sizeof(Evas_Object_Textblock_Line));
1926    c->align = fmt->halign;
1927    c->align_auto = fmt->halign_auto;
1928    c->marginl = fmt->margin.l;
1929    c->marginr = fmt->margin.r;
1930    c->par->lines = (Evas_Object_Textblock_Line *)eina_inlist_append(EINA_INLIST_GET(c->par->lines), EINA_INLIST_GET(c->ln));
1931    c->x = 0;
1932    c->maxascent = c->maxdescent = 0;
1933    c->ln->line_no = -1;
1934    c->ln->par = c->par;
1935 }
1936
1937 static inline Evas_Object_Textblock_Paragraph *
1938 _layout_find_paragraph_by_y(Evas_Object_Textblock *o, Evas_Coord y)
1939 {
1940    Evas_Object_Textblock_Paragraph *start, *par;
1941    int i;
1942
1943    start = o->paragraphs;
1944
1945    for (i = 0 ; i < TEXTBLOCK_PAR_INDEX_SIZE ; i++)
1946      {
1947         if (!o->par_index[i] || (o->par_index[i]->y > y))
1948           {
1949              break;
1950           }
1951         start = o->par_index[i];
1952      }
1953
1954    EINA_INLIST_FOREACH(start, par)
1955      {
1956         if ((par->y <= y) && (y < par->y + par->h))
1957            return par;
1958      }
1959
1960    return NULL;
1961 }
1962
1963 static inline Evas_Object_Textblock_Paragraph *
1964 _layout_find_paragraph_by_line_no(Evas_Object_Textblock *o, int line_no)
1965 {
1966    Evas_Object_Textblock_Paragraph *start, *par;
1967    int i;
1968
1969    start = o->paragraphs;
1970
1971    for (i = 0 ; i < TEXTBLOCK_PAR_INDEX_SIZE ; i++)
1972      {
1973         if (!o->par_index[i] || (o->par_index[i]->line_no > line_no))
1974           {
1975              break;
1976           }
1977         start = o->par_index[i];
1978      }
1979
1980    EINA_INLIST_FOREACH(start, par)
1981      {
1982         Evas_Object_Textblock_Paragraph *npar =
1983            (Evas_Object_Textblock_Paragraph *) EINA_INLIST_GET(par)->next;
1984         if ((par->line_no <= line_no) &&
1985               (!npar || (line_no < npar->line_no)))
1986            return par;
1987      }
1988
1989    return NULL;
1990 }
1991 /* End of rbtree index functios */
1992
1993 /**
1994  * @internal
1995  * Create a new layout paragraph.
1996  * If c->par is not NULL, the paragraph is appended/prepended according
1997  * to the append parameter. If it is NULL, the paragraph is appended at
1998  * the end of the list.
1999  *
2000  * @param c The context to work on - Not NULL.
2001  * @param n the associated text node
2002  * @param append true to append, false to prpend.
2003  */
2004 static void
2005 _layout_paragraph_new(Ctxt *c, Evas_Object_Textblock_Node_Text *n,
2006       Eina_Bool append)
2007 {
2008    Evas_Object_Textblock_Paragraph *rel_par = c->par;
2009    c->par = calloc(1, sizeof(Evas_Object_Textblock_Paragraph));
2010    if (append || !rel_par)
2011       c->paragraphs = (Evas_Object_Textblock_Paragraph *)
2012          eina_inlist_append_relative(EINA_INLIST_GET(c->paragraphs),
2013                EINA_INLIST_GET(c->par),
2014                EINA_INLIST_GET(rel_par));
2015    else
2016       c->paragraphs = (Evas_Object_Textblock_Paragraph *)
2017          eina_inlist_prepend_relative(EINA_INLIST_GET(c->paragraphs),
2018                EINA_INLIST_GET(c->par),
2019                EINA_INLIST_GET(rel_par));
2020
2021    c->ln = NULL;
2022    c->par->text_node = n;
2023    if (n)
2024       n->par = c->par;
2025    c->par->line_no = -1;
2026    c->par->visible = 1;
2027    c->o->num_paragraphs++;
2028 }
2029
2030 #ifdef BIDI_SUPPORT
2031 /**
2032  * @internal
2033  * Update bidi paragraph props.
2034  *
2035  * @param par The paragraph to update
2036  */
2037 static inline void
2038 _layout_update_bidi_props(const Evas_Object_Textblock *o,
2039       Evas_Object_Textblock_Paragraph *par)
2040 {
2041    if (par->text_node)
2042      {
2043         const Eina_Unicode *text;
2044         int *segment_idxs = NULL;
2045         text = eina_ustrbuf_string_get(par->text_node->unicode);
2046
2047         if (o->bidi_delimiters)
2048            segment_idxs = evas_bidi_segment_idxs_get(text, o->bidi_delimiters);
2049
2050         evas_bidi_paragraph_props_unref(par->bidi_props);
2051         par->bidi_props = evas_bidi_paragraph_props_get(text,
2052               eina_ustrbuf_length_get(par->text_node->unicode),
2053               segment_idxs);
2054         par->direction = EVAS_BIDI_PARAGRAPH_DIRECTION_IS_RTL(par->bidi_props) ?
2055            EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
2056         par->is_bidi = !!par->bidi_props;
2057         if (segment_idxs) free(segment_idxs);
2058      }
2059 }
2060 #endif
2061
2062
2063 /**
2064  * @internal
2065  * Free the visual lines in the paragraph (logical items are kept)
2066  */
2067 static void
2068 _paragraph_clear(const Evas_Object *obj __UNUSED__,
2069       Evas_Object_Textblock_Paragraph *par)
2070 {
2071    while (par->lines)
2072      {
2073         Evas_Object_Textblock_Line *ln;
2074
2075         ln = (Evas_Object_Textblock_Line *) par->lines;
2076         par->lines = (Evas_Object_Textblock_Line *)eina_inlist_remove(EINA_INLIST_GET(par->lines), EINA_INLIST_GET(par->lines));
2077         _line_free(ln);
2078      }
2079 }
2080
2081 /**
2082  * @internal
2083  * Free the layout paragraph and all of it's lines and logical items.
2084  */
2085 static void
2086 _paragraph_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *par)
2087 {
2088    Evas_Object_Textblock *o;
2089    o = (Evas_Object_Textblock *)(obj->object_data);
2090    _paragraph_clear(obj, par);
2091
2092      {
2093         Eina_List *i, *i_prev;
2094         Evas_Object_Textblock_Item *it;
2095         EINA_LIST_FOREACH_SAFE(par->logical_items, i, i_prev, it)
2096           {
2097              _item_free(obj, NULL, it);
2098           }
2099         eina_list_free(par->logical_items);
2100      }
2101 #ifdef BIDI_SUPPORT
2102    if (par->bidi_props)
2103       evas_bidi_paragraph_props_unref(par->bidi_props);
2104 #endif
2105    /* If we are the active par of the text node, set to NULL */
2106    if (par->text_node && (par->text_node->par == par))
2107       par->text_node->par = NULL;
2108
2109    o->num_paragraphs--;
2110
2111    free(par);
2112 }
2113
2114 /**
2115  * @internal
2116  * Clear all the paragraphs from the inlist pars.
2117  *
2118  * @param obj the evas object - Not NULL.
2119  * @param pars the paragraphs to clean - Not NULL.
2120  */
2121 static void
2122 _paragraphs_clear(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *pars)
2123 {
2124    Evas_Object_Textblock_Paragraph *par;
2125
2126    EINA_INLIST_FOREACH(EINA_INLIST_GET(pars), par)
2127      {
2128         _paragraph_clear(obj, par);
2129      }
2130 }
2131
2132 /**
2133  * @internal
2134  * Free the paragraphs from the inlist pars, the difference between this and
2135  * _paragraphs_clear is that the latter keeps the logical items and the par
2136  * items, while the former frees them as well.
2137  *
2138  * @param obj the evas object - Not NULL.
2139  * @param pars the paragraphs to clean - Not NULL.
2140  */
2141 static void
2142 _paragraphs_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *pars)
2143 {
2144    Evas_Object_Textblock *o;
2145    o = (Evas_Object_Textblock *)(obj->object_data);
2146
2147    o->num_paragraphs = 0;
2148
2149    while (pars)
2150      {
2151         Evas_Object_Textblock_Paragraph *par;
2152
2153         par = (Evas_Object_Textblock_Paragraph *) pars;
2154         pars = (Evas_Object_Textblock_Paragraph *)eina_inlist_remove(EINA_INLIST_GET(pars), EINA_INLIST_GET(par));
2155         _paragraph_free(obj, par);
2156      }
2157 }
2158
2159 /**
2160  * @internal
2161  * Push fmt to the format stack, if fmt is NULL, will fush a default item.
2162  *
2163  * @param c the context to work on - Not NULL.
2164  * @param fmt the format to push.
2165  * @see _layout_format_pop()
2166  */
2167 static Evas_Object_Textblock_Format *
2168 _layout_format_push(Ctxt *c, Evas_Object_Textblock_Format *fmt,
2169       Evas_Object_Textblock_Node_Format *fnode)
2170 {
2171    if (fmt)
2172      {
2173         fmt = _format_dup(c->obj, fmt);
2174         c->format_stack  = eina_list_prepend(c->format_stack, fmt);
2175         fmt->fnode = fnode;
2176      }
2177    else
2178      {
2179         fmt = calloc(1, sizeof(Evas_Object_Textblock_Format));
2180         c->format_stack  = eina_list_prepend(c->format_stack, fmt);
2181         fmt->ref = 1;
2182         fmt->halign = 0.0;
2183         fmt->halign_auto = EINA_TRUE;
2184         fmt->valign = -1.0;
2185         fmt->style = EVAS_TEXT_STYLE_PLAIN;
2186         fmt->tabstops = 32;
2187         fmt->linesize = 0;
2188         fmt->linerelsize = 0.0;
2189         fmt->linegap = 0;
2190         fmt->underline_dash_width = 6;
2191         fmt->underline_dash_gap = 2;
2192         fmt->linerelgap = 0.0;
2193         fmt->password = 1;
2194      }
2195    return fmt;
2196 }
2197
2198 /**
2199  * @internal
2200  * Pop fmt to the format stack, if there's something in the stack free fmt
2201  * and set it to point to the next item instead, else return fmt.
2202  *
2203  * @param c the context to work on - Not NULL.
2204  * @param format - the text of the format to free (assured to start with '-').
2205  * @return the next format in the stack, or format if there's none.
2206  * @see _layout_format_push()
2207  */
2208 static Evas_Object_Textblock_Format *
2209 _layout_format_pop(Ctxt *c, const char *format)
2210 {
2211    Evas_Object_Textblock_Format *fmt = eina_list_data_get(c->format_stack);
2212
2213    if ((c->format_stack) && (c->format_stack->next))
2214      {
2215         Eina_List *redo_nodes = NULL;
2216
2217         /* Generic pop, should just pop. */
2218         if (((format[0] == ' ') && !format[1]) ||
2219               !format[0])
2220           {
2221              _format_unref_free(c->obj, fmt);
2222              c->format_stack =
2223                 eina_list_remove_list(c->format_stack, c->format_stack);
2224           }
2225         else
2226           {
2227              size_t len = strlen(format);
2228              Eina_List *i, *i_next;
2229              /* Remove only the matching format. */
2230              EINA_LIST_FOREACH_SAFE(c->format_stack, i, i_next, fmt)
2231                {
2232                   /* Stop when we reach the base item */
2233                   if (!i_next)
2234                      break;
2235
2236                   c->format_stack =
2237                      eina_list_remove_list(c->format_stack, c->format_stack);
2238
2239                   /* Make sure the ending tag matches the starting tag.
2240                    * I.e whole of the ending tag matches the start of the
2241                    * starting tag, and the starting tag's next char is either
2242                    * NULL or white. Skip the starting '+'. */
2243                   if (_FORMAT_IS_CLOSER_OF(
2244                            fmt->fnode->orig_format, format, len))
2245                     {
2246                        _format_unref_free(c->obj, fmt);
2247                        break;
2248                     }
2249                   else
2250                     {
2251                        redo_nodes = eina_list_prepend(redo_nodes, fmt->fnode);
2252                        _format_unref_free(c->obj, fmt);
2253                     }
2254                }
2255           }
2256
2257         /* Redo all the nodes needed to be redone */
2258           {
2259              Evas_Object_Textblock_Node_Format *fnode;
2260              Eina_List *i, *i_next;
2261
2262              EINA_LIST_FOREACH_SAFE(redo_nodes, i, i_next, fnode)
2263                {
2264                   /* FIXME: Actually do something with the new acquired padding,
2265                    * the can be different and affect our padding! */
2266                   Evas_Coord style_pad_l, style_pad_r, style_pad_t, style_pad_b;
2267                   style_pad_l = style_pad_r = style_pad_t = style_pad_b = 0;
2268                   redo_nodes = eina_list_remove_list(redo_nodes, i);
2269                   fmt = eina_list_data_get(c->format_stack);
2270                   _layout_do_format(c->obj, c, &fmt, fnode,
2271                         &style_pad_l, &style_pad_r,
2272                         &style_pad_t, &style_pad_b, EINA_FALSE);
2273                }
2274           }
2275
2276         fmt = eina_list_data_get(c->format_stack);
2277      }
2278    return fmt;
2279 }
2280
2281 /**
2282  * @internal
2283  * Parse item and fill fmt with the item.
2284  *
2285  * @param c the context to work on - Not NULL.
2286  * @param fmt the format to fill - not null.
2287  */
2288 static void
2289 _layout_format_value_handle(Ctxt *c, Evas_Object_Textblock_Format *fmt, const char *item)
2290 {
2291    const char *key = NULL, *val = NULL;
2292
2293    _format_param_parse(item, &key, &val);
2294    if ((key) && (val)) _format_command(c->obj, fmt, key, val);
2295    if (key) eina_stringshare_del(key);
2296    if (val) eina_stringshare_del(val);
2297    c->align = fmt->halign;
2298    c->align_auto = fmt->halign_auto;
2299    c->marginl = fmt->margin.l;
2300    c->marginr = fmt->margin.r;
2301 }
2302
2303 #define VSIZE_FULL 0
2304 #define VSIZE_ASCENT 1
2305
2306 #define SIZE 0
2307 #define SIZE_ABS 1
2308 #define SIZE_REL 2
2309
2310 /**
2311  * @internal
2312  * Get the current line's alignment from the context.
2313  *
2314  * @param c the context to work on - Not NULL.
2315  */
2316 static inline double
2317 _layout_line_align_get(Ctxt *c)
2318 {
2319 #ifdef BIDI_SUPPORT
2320    if (c->align_auto && c->ln)
2321      {
2322         if (c->ln->items && c->ln->items->text_node &&
2323               (c->ln->par->direction == EVAS_BIDI_DIRECTION_RTL))
2324           {
2325              /* Align right*/
2326              return 1.0;
2327           }
2328         else
2329           {
2330              /* Align left */
2331              return 0.0;
2332           }
2333      }
2334 #endif
2335    return c->align;
2336 }
2337
2338 #ifdef BIDI_SUPPORT
2339 /**
2340  * @internal
2341  * Reorder the items in visual order
2342  *
2343  * @param line the line to reorder
2344  */
2345 static void
2346 _layout_line_reorder(Evas_Object_Textblock_Line *line)
2347 {
2348    /*FIXME: do it a bit more efficient - not very efficient ATM. */
2349    Evas_Object_Textblock_Item *it;
2350    EvasBiDiStrIndex *v_to_l = NULL;
2351    Evas_Coord x;
2352    size_t start, end;
2353    size_t len;
2354
2355    if (line->items && line->items->text_node &&
2356          line->par->bidi_props)
2357      {
2358         Evas_BiDi_Paragraph_Props *props;
2359         props = line->par->bidi_props;
2360         start = end = line->items->text_pos;
2361
2362         /* Find the first and last positions in the line */
2363
2364         EINA_INLIST_FOREACH(line->items, it)
2365           {
2366              if (it->text_pos < start)
2367                {
2368                   start = it->text_pos;
2369                }
2370              else
2371                {
2372                   int tlen;
2373                   tlen = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
2374                      _ITEM_TEXT(it)->text_props.text_len : 1;
2375                   if (it->text_pos + tlen > end)
2376                     {
2377                        end = it->text_pos + tlen;
2378                     }
2379                }
2380           }
2381
2382         len = end - start;
2383         evas_bidi_props_reorder_line(NULL, start, len, props, &v_to_l);
2384
2385         /* Update visual pos */
2386           {
2387              Evas_Object_Textblock_Item *i;
2388              i = line->items;
2389              while (i)
2390                {
2391                   i->visual_pos = evas_bidi_position_logical_to_visual(
2392                         v_to_l, len, i->text_pos - start);
2393                   i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(i)->next;
2394                }
2395           }
2396
2397         /*FIXME: not very efficient, sort the items arrays. Anyhow, should only
2398          * reorder if it's a bidi paragraph */
2399           {
2400              Evas_Object_Textblock_Item *i, *j, *min;
2401              i = line->items;
2402              while (i)
2403                {
2404                   min = i;
2405                   EINA_INLIST_FOREACH(i, j)
2406                     {
2407                        if (j->visual_pos < min->visual_pos)
2408                          {
2409                             min = j;
2410                          }
2411                     }
2412                   if (min != i)
2413                     {
2414                        line->items = (Evas_Object_Textblock_Item *) eina_inlist_remove(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min));
2415                        line->items = (Evas_Object_Textblock_Item *) eina_inlist_prepend_relative(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min), EINA_INLIST_GET(i));
2416                     }
2417
2418                   i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(min)->next;
2419                }
2420           }
2421      }
2422
2423    if (v_to_l) free(v_to_l);
2424    x = 0;
2425    EINA_INLIST_FOREACH(line->items, it)
2426      {
2427         it->x = x;
2428         x += it->adv;
2429      }
2430 }
2431 #endif
2432
2433 /* FIXME: doc */
2434 static void
2435 _layout_calculate_format_item_size(const Evas_Object *obj,
2436       const Evas_Object_Textblock_Format_Item *fi,
2437       Evas_Coord *maxascent, Evas_Coord *maxdescent,
2438       Evas_Coord *_y, Evas_Coord *_w, Evas_Coord *_h)
2439 {
2440    /* Adjust sizes according to current line height/scale */
2441    Evas_Coord w, h;
2442    const char *p, *s;
2443
2444    s = fi->item;
2445    w = fi->parent.w;
2446    h = fi->parent.h;
2447    switch (fi->size)
2448      {
2449       case SIZE:
2450          p = strstr(s, " size=");
2451          if (p)
2452            {
2453               p += 6;
2454               if (sscanf(p, "%ix%i", &w, &h) == 2)
2455                 {
2456                    w = w * obj->cur.scale;
2457                    h = h * obj->cur.scale;
2458                 }
2459            }
2460          break;
2461       case SIZE_REL:
2462          p = strstr((char *) s, " relsize=");
2463          p += 9;
2464          if (sscanf(p, "%ix%i", &w, &h) == 2)
2465            {
2466               int sz = 1;
2467               if (fi->vsize == VSIZE_FULL)
2468                 {
2469                    sz = *maxdescent + *maxascent;
2470                 }
2471               else if (fi->vsize == VSIZE_ASCENT)
2472                 {
2473                    sz = *maxascent;
2474                 }
2475               w = (w * sz) / h;
2476               h = sz;
2477            }
2478          break;
2479       case SIZE_ABS:
2480          /* Nothing to do */
2481       default:
2482          break;
2483      }
2484
2485    switch (fi->size)
2486      {
2487       case SIZE:
2488       case SIZE_ABS:
2489          switch (fi->vsize)
2490            {
2491             case VSIZE_FULL:
2492                if (h > (*maxdescent + *maxascent))
2493                  {
2494                     *maxascent += h - (*maxdescent + *maxascent);
2495                     *_y = -*maxascent;
2496                  }
2497                else
2498                   *_y = -(h - *maxdescent);
2499                break;
2500             case VSIZE_ASCENT:
2501                if (h > *maxascent)
2502                  {
2503                     *maxascent = h;
2504                     *_y = -h;
2505                  }
2506                else
2507                   *_y = -h;
2508                break;
2509             default:
2510                break;
2511            }
2512          break;
2513       case SIZE_REL:
2514          switch (fi->vsize)
2515            {
2516             case VSIZE_FULL:
2517             case VSIZE_ASCENT:
2518                *_y = -*maxascent;
2519                break;
2520             default:
2521                break;
2522            }
2523          break;
2524       default:
2525          break;
2526      }
2527
2528    *_w = w;
2529    *_h = h;
2530 }
2531
2532 /**
2533  * @internal
2534  * Order the items in the line, update it's properties and update it's
2535  * corresponding paragraph.
2536  *
2537  * @param c the context to work on - Not NULL.
2538  * @param fmt the format to use.
2539  * @param add_line true if we should create a line, false otherwise.
2540  */
2541 static void
2542 _layout_line_finalize(Ctxt *c, Evas_Object_Textblock_Format *fmt)
2543 {
2544    Evas_Object_Textblock_Item *it;
2545    Evas_Coord x = 0;
2546
2547    /* If there are no text items yet, calc ascent/descent
2548     * according to the current format. */
2549    if (c->maxascent + c->maxdescent == 0)
2550       _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
2551             &c->maxdescent, fmt);
2552
2553    /* Adjust all the item sizes according to the final line size,
2554     * and update the x positions of all the items of the line. */
2555    EINA_INLIST_FOREACH(c->ln->items, it)
2556      {
2557         if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
2558           {
2559              Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
2560              if (!fi->formatme) goto loop_advance;
2561              _layout_calculate_format_item_size(c->obj, fi, &c->maxascent,
2562                    &c->maxdescent, &fi->y, &fi->parent.w, &fi->parent.h);
2563              fi->parent.adv = fi->parent.w;
2564           }
2565
2566 loop_advance:
2567         it->x = x;
2568         x += it->adv;
2569
2570         if ((it->x + it->adv) > c->ln->w) c->ln->w = it->x + it->adv;
2571      }
2572
2573    c->ln->y = (c->y - c->par->y) + c->o->style_pad.t;
2574    c->ln->h = c->maxascent + c->maxdescent;
2575    c->ln->baseline = c->maxascent;
2576    if (c->have_underline2)
2577      {
2578         if (c->maxdescent < 4) c->underline_extend = 4 - c->maxdescent;
2579      }
2580    else if (c->have_underline)
2581      {
2582         if (c->maxdescent < 2) c->underline_extend = 2 - c->maxdescent;
2583      }
2584    c->ln->line_no = c->line_no - c->ln->par->line_no;
2585    c->line_no++;
2586    c->y += c->maxascent + c->maxdescent;
2587    if (c->w >= 0)
2588      {
2589         c->ln->x = c->marginl + c->o->style_pad.l +
2590            ((c->w - c->ln->w -
2591              c->o->style_pad.l - c->o->style_pad.r -
2592              c->marginl - c->marginr) * _layout_line_align_get(c));
2593      }
2594    else
2595      {
2596         c->ln->x = c->marginl + c->o->style_pad.l;
2597      }
2598
2599    c->par->h = c->ln->y + c->ln->h;
2600    if (c->ln->w > c->par->w)
2601      c->par->w = c->ln->w;
2602
2603      {
2604         Evas_Coord new_wmax = c->ln->w +
2605            c->marginl + c->marginr - (c->o->style_pad.l + c->o->style_pad.r);
2606         if (new_wmax > c->wmax)
2607            c->wmax = new_wmax;
2608      }
2609 }
2610
2611 /**
2612  * @internal
2613  * Create a new line and append it to the lines in the context.
2614  *
2615  * @param c the context to work on - Not NULL.
2616  * @param fmt the format to use.
2617  * @param add_line true if we should create a line, false otherwise.
2618  */
2619 static void
2620 _layout_line_advance(Ctxt *c, Evas_Object_Textblock_Format *fmt)
2621 {
2622    _layout_line_finalize(c, fmt);
2623    _layout_line_new(c, fmt);
2624 }
2625
2626 /**
2627  * @internal
2628  * Create a new text layout item from the string and the format.
2629  *
2630  * @param c the context to work on - Not NULL.
2631  * @param fmt the format to use.
2632  * @param str the string to use.
2633  * @param len the length of the string.
2634  */
2635 static Evas_Object_Textblock_Text_Item *
2636 _layout_text_item_new(Ctxt *c __UNUSED__, Evas_Object_Textblock_Format *fmt)
2637 {
2638    Evas_Object_Textblock_Text_Item *ti;
2639
2640    ti = calloc(1, sizeof(Evas_Object_Textblock_Text_Item));
2641    ti->parent.format = fmt;
2642    ti->parent.format->ref++;
2643    ti->parent.type = EVAS_TEXTBLOCK_ITEM_TEXT;
2644    return ti;
2645 }
2646
2647 /**
2648  * @internal
2649  * Return the cutoff of the text in the text item.
2650  *
2651  * @param c the context to work on - Not NULL.
2652  * @param fmt the format to use. - Not NULL.
2653  * @param it the item to check - Not null.
2654  * @return -1 if there is no cutoff (either because there is really none,
2655  * or because of an error), cutoff index on success.
2656  */
2657 static int
2658 _layout_text_cutoff_get(Ctxt *c, Evas_Object_Textblock_Format *fmt,
2659       const Evas_Object_Textblock_Text_Item *ti)
2660 {
2661    if (fmt->font.font)
2662      {
2663         Evas_Coord x;
2664         x = c->w - c->o->style_pad.l - c->o->style_pad.r - c->marginl -
2665            c->marginr - c->x - ti->x_adjustment;
2666         if (x < 0)
2667           x = 0;
2668         return c->ENFN->font_last_up_to_pos(c->ENDT, fmt->font.font,
2669               &ti->text_props, x, 0);
2670      }
2671    return -1;
2672 }
2673
2674 /**
2675  * @internal
2676  * Split before cut, and strip if str[cut - 1] is a whitespace.
2677  *
2678  * @param c the context to work on - Not NULL.
2679  * @param ti the item to cut - not null.
2680  * @param lti the logical list item of the item.
2681  * @param cut the cut index.
2682  * @return the second (newly created) item.
2683  */
2684 static Evas_Object_Textblock_Text_Item *
2685 _layout_item_text_split_strip_white(Ctxt *c,
2686       Evas_Object_Textblock_Text_Item *ti, Eina_List *lti, size_t cut)
2687 {
2688    const Eina_Unicode *ts;
2689    Evas_Object_Textblock_Text_Item *new_ti = NULL, *white_ti = NULL;
2690
2691    ts = GET_ITEM_TEXT(ti);
2692
2693    if (!IS_AT_END(ti, cut) && (ti->text_props.text_len > 0))
2694      {
2695         new_ti = _layout_text_item_new(c, ti->parent.format);
2696         new_ti->parent.text_node = ti->parent.text_node;
2697         new_ti->parent.text_pos = ti->parent.text_pos + cut;
2698         new_ti->parent.merge = EINA_TRUE;
2699
2700         evas_common_text_props_split(&ti->text_props,
2701                                      &new_ti->text_props, cut);
2702         _layout_text_add_logical_item(c, new_ti, lti);
2703      }
2704
2705    /* Strip the previous white if needed */
2706    if ((cut >= 1) && _is_white(ts[cut - 1]) && (ti->text_props.text_len > 0))
2707      {
2708         if (cut - 1 > 0)
2709           {
2710              size_t white_cut = cut - 1;
2711              white_ti = _layout_text_item_new(c, ti->parent.format);
2712              white_ti->parent.text_node = ti->parent.text_node;
2713              white_ti->parent.text_pos = ti->parent.text_pos + white_cut;
2714              white_ti->parent.merge = EINA_TRUE;
2715              white_ti->parent.visually_deleted = EINA_TRUE;
2716
2717              evas_common_text_props_split(&ti->text_props,
2718                    &white_ti->text_props, white_cut);
2719              _layout_text_add_logical_item(c, white_ti, lti);
2720           }
2721         else
2722           {
2723              /* Mark this one as the visually deleted. */
2724              ti->parent.visually_deleted = EINA_TRUE;
2725           }
2726      }
2727
2728    if (new_ti || white_ti)
2729      {
2730         _text_item_update_sizes(c, ti);
2731      }
2732    return new_ti;
2733 }
2734
2735 /**
2736  * @internal
2737  * Merge item2 into item1 and free item2.
2738  *
2739  * @param c the context to work on - Not NULL.
2740  * @param item1 the item to copy to
2741  * @param item2 the item to copy from
2742  */
2743 static void
2744 _layout_item_merge_and_free(Ctxt *c,
2745       Evas_Object_Textblock_Text_Item *item1,
2746       Evas_Object_Textblock_Text_Item *item2)
2747 {
2748    evas_common_text_props_merge(&item1->text_props,
2749          &item2->text_props);
2750
2751    _text_item_update_sizes(c, item1);
2752
2753    item1->parent.merge = EINA_FALSE;
2754    item1->parent.visually_deleted = EINA_FALSE;
2755
2756    _item_free(c->obj, NULL, _ITEM(item2));
2757 }
2758
2759 /**
2760  * @internal
2761  * Calculates an item's size.
2762  *
2763  * @param c the context
2764  * @param it the item itself.
2765  */
2766 static void
2767 _text_item_update_sizes(Ctxt *c, Evas_Object_Textblock_Text_Item *ti)
2768 {
2769    int tw, th, inset, advw;
2770    const Evas_Object_Textblock_Format *fmt = ti->parent.format;
2771    int shad_sz = 0, shad_dst = 0, out_sz = 0;
2772    int dx = 0, minx = 0, maxx = 0, shx1, shx2;
2773
2774    tw = th = 0;
2775    if (fmt->font.font)
2776      c->ENFN->font_string_size_get(c->ENDT, fmt->font.font,
2777            &ti->text_props, &tw, &th);
2778    inset = 0;
2779    if (fmt->font.font)
2780      inset = c->ENFN->font_inset_get(c->ENDT, fmt->font.font,
2781            &ti->text_props);
2782    advw = 0;
2783    if (fmt->font.font)
2784       advw = c->ENFN->font_h_advance_get(c->ENDT, fmt->font.font,
2785            &ti->text_props);
2786
2787
2788    /* These adjustments are calculated and thus heavily linked to those in
2789     * textblock_render!!! Don't change one without the other. */
2790
2791    switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC)
2792      {
2793       case EVAS_TEXT_STYLE_SHADOW:
2794         shad_dst = 1;
2795         break;
2796       case EVAS_TEXT_STYLE_OUTLINE_SHADOW:
2797       case EVAS_TEXT_STYLE_FAR_SHADOW:
2798         shad_dst = 2;
2799         out_sz = 1;
2800         break;
2801       case EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW:
2802         shad_dst = 1;
2803         shad_sz = 2;
2804         out_sz = 1;
2805         break;
2806       case EVAS_TEXT_STYLE_FAR_SOFT_SHADOW:
2807         shad_dst = 2;
2808         shad_sz = 2;
2809         break;
2810       case EVAS_TEXT_STYLE_SOFT_SHADOW:
2811         shad_dst = 1;
2812         shad_sz = 2;
2813         break;
2814       case EVAS_TEXT_STYLE_GLOW:
2815       case EVAS_TEXT_STYLE_SOFT_OUTLINE:
2816         out_sz = 2;
2817         break;
2818       case EVAS_TEXT_STYLE_OUTLINE:
2819         out_sz = 1;
2820         break;
2821       default:
2822         break;
2823      }
2824    switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_SHADOW_DIRECTION)
2825      {
2826       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT:
2827       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT:
2828       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT:
2829         dx = -1;
2830         break;
2831       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT:
2832       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT:
2833       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT:
2834         dx = 1;
2835         break;
2836       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP:
2837       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM:
2838       default:
2839         dx = 0;
2840         break;
2841      }
2842    minx = -out_sz;
2843    maxx = out_sz;
2844    shx1 = dx * shad_dst;
2845    shx1 -= shad_sz;
2846    shx2 = dx * shad_dst;
2847    shx2 += shad_sz;
2848    if (shx1 < minx) minx = shx1;
2849    if (shx2 > maxx) maxx = shx2;
2850    inset += -minx;
2851    ti->x_adjustment = maxx - minx;
2852    
2853    ti->inset = inset;
2854    ti->parent.w = tw + ti->x_adjustment;
2855    ti->parent.h = th;
2856    ti->parent.adv = advw;
2857    ti->parent.x = 0;
2858 }
2859
2860 /**
2861  * @internal
2862  * Adds the item to the list, updates the item's properties (e.g, x,w,h)
2863  *
2864  * @param c the context
2865  * @param it the item itself.
2866  * @param rel item ti will be appened after, NULL = last.
2867  */
2868 static void
2869 _layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Text_Item *ti,
2870       Eina_List *rel)
2871 {
2872    _text_item_update_sizes(c, ti);
2873
2874    c->par->logical_items = eina_list_append_relative_list(
2875          c->par->logical_items, ti, rel);
2876 }
2877
2878 /**
2879  * @internal
2880  * Appends the text from node n starting at start ending at off to the layout.
2881  * It uses the fmt for the formatting.
2882  *
2883  * @param c the current context- NOT NULL.
2884  * @param fmt the format to use.
2885  * @param n the text node. - Not null.
2886  * @param start the start position. - in range.
2887  * @param off the offset - start + offset in range. if offset is -1, it'll add everything to the end of the string if offset = 0 it'll return with doing nothing.
2888  * @param repch a replacement char to print instead of the original string, for example, * when working with passwords.
2889  */
2890 static void
2891 _layout_text_append(Ctxt *c, Evas_Object_Textblock_Format *fmt, Evas_Object_Textblock_Node_Text *n, int start, int off, const char *repch)
2892 {
2893    const Eina_Unicode *str = EINA_UNICODE_EMPTY_STRING;
2894    const Eina_Unicode *tbase;
2895    Evas_Object_Textblock_Text_Item *ti;
2896    size_t cur_len = 0;
2897    Eina_Unicode urepch = 0;
2898
2899    /* prepare a working copy of the string, either filled by the repch or
2900     * filled with the true values */
2901    if (n)
2902      {
2903         int len;
2904         int orig_off = off;
2905
2906         /* Figure out if we want to bail, work with an empty string,
2907          * or continue with a slice of the passed string */
2908         len = eina_ustrbuf_length_get(n->unicode);
2909         if (off == 0) return;
2910         else if (off < 0) off = len - start;
2911
2912         if (start < 0)
2913           {
2914              start = 0;
2915           }
2916         else if ((start == 0) && (off == 0) && (orig_off == -1))
2917           {
2918              /* Special case that means that we need to add an empty
2919               * item */
2920              str = EINA_UNICODE_EMPTY_STRING;
2921              goto skip;
2922           }
2923         else if ((start >= len) || (start + off > len))
2924           {
2925              return;
2926           }
2927
2928         /* If we work with a replacement char, create a string which is the same
2929          * but with replacement chars instead of regular chars. */
2930         if ((fmt->password) && (repch) && (eina_ustrbuf_length_get(n->unicode)))
2931           {
2932              int i, ind;
2933              Eina_Unicode *ptr;
2934
2935              tbase = str = ptr = alloca((off + 1) * sizeof(Eina_Unicode));
2936              ind = 0;
2937              urepch = eina_unicode_utf8_get_next(repch, &ind);
2938              for (i = 0 ; i < off; ptr++, i++)
2939                *ptr = urepch;
2940              *ptr = 0;
2941           }
2942         /* Use the string, just cut the relevant parts */
2943         else
2944           {
2945              str = eina_ustrbuf_string_get(n->unicode) + start;
2946           }
2947
2948         cur_len = off;
2949      }
2950
2951 skip:
2952    tbase = str;
2953
2954    /* If there's no parent text node, only create an empty item */
2955    if (!n)
2956      {
2957         ti = _layout_text_item_new(c, fmt);
2958         ti->parent.text_node = NULL;
2959         ti->parent.text_pos = 0;
2960         _layout_text_add_logical_item(c, ti, NULL);
2961
2962         return;
2963      }
2964
2965    while (cur_len > 0)
2966      {
2967         Evas_Font_Instance *script_fi = NULL;
2968         int script_len, tmp_cut;
2969         Evas_Script_Type script;
2970
2971         script_len = cur_len;
2972
2973         tmp_cut = evas_common_language_script_end_of_run_get(str,
2974               c->par->bidi_props, start + str - tbase, script_len);
2975         if (tmp_cut > 0)
2976           {
2977              script_len = tmp_cut;
2978           }
2979         cur_len -= script_len;
2980
2981         script = evas_common_language_script_type_get(str, script_len);
2982
2983
2984         while (script_len > 0)
2985           {
2986              Evas_Font_Instance *cur_fi = NULL;
2987              int run_len = script_len;
2988              ti = _layout_text_item_new(c, fmt);
2989              ti->parent.text_node = n;
2990              ti->parent.text_pos = start + str - tbase;
2991
2992              if (ti->parent.format->font.font)
2993                {
2994                   run_len = c->ENFN->font_run_end_get(c->ENDT,
2995                         ti->parent.format->font.font, &script_fi, &cur_fi,
2996                         script, str, script_len);
2997                }
2998
2999              evas_common_text_props_bidi_set(&ti->text_props,
3000                    c->par->bidi_props, ti->parent.text_pos);
3001              evas_common_text_props_script_set(&ti->text_props, script);
3002
3003              if (cur_fi)
3004                {
3005                   c->ENFN->font_text_props_info_create(c->ENDT,
3006                         cur_fi, str, &ti->text_props, c->par->bidi_props,
3007                         ti->parent.text_pos, run_len, EVAS_TEXT_PROPS_MODE_SHAPE);
3008                }
3009              str += run_len;
3010              script_len -= run_len;
3011
3012              _layout_text_add_logical_item(c, ti, NULL);
3013           }
3014      }
3015 }
3016
3017 /**
3018  * @internal
3019  * Add a format item from the format node n and the item item.
3020  *
3021  * @param c the current context- NOT NULL.
3022  * @param n the source format node - not null.
3023  * @param item the format text.
3024  *
3025  * @return the new format item.
3026  */
3027 static Evas_Object_Textblock_Format_Item *
3028 _layout_format_item_add(Ctxt *c, Evas_Object_Textblock_Node_Format *n, const char *item, Evas_Object_Textblock_Format *fmt)
3029 {
3030    Evas_Object_Textblock_Format_Item *fi;
3031
3032    fi = calloc(1, sizeof(Evas_Object_Textblock_Format_Item));
3033    fi->item = eina_stringshare_add(item);
3034    fi->parent.type = EVAS_TEXTBLOCK_ITEM_FORMAT;
3035    fi->parent.format = fmt;
3036    fi->parent.format->ref++;
3037    c->par->logical_items = eina_list_append(c->par->logical_items, fi);
3038    if (n)
3039      {
3040         fi->parent.text_node = n->text_node;
3041         /* FIXME: make it more efficient */
3042         fi->parent.text_pos = _evas_textblock_node_format_pos_get(n);
3043 #ifdef BIDI_SUPPORT
3044         fi->bidi_dir = (evas_bidi_is_rtl_char(
3045               c->par->bidi_props,
3046               0,
3047               fi->parent.text_pos)) ?
3048            EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
3049 #else
3050         fi->bidi_dir = EVAS_BIDI_DIRECTION_LTR;
3051 #endif
3052      }
3053    return fi;
3054 }
3055
3056 /**
3057  * @internal
3058  * Should be call after we finish filling a format.
3059  * FIXME: doc.
3060  */
3061 static void
3062 _format_finalize(Evas_Object *obj, Evas_Object_Textblock_Format *fmt)
3063 {
3064    void *of;
3065
3066    of = fmt->font.font;
3067
3068    fmt->font.font = evas_font_load(obj->layer->evas, fmt->font.fdesc,
3069          fmt->font.source, (int)(((double) fmt->font.size) * obj->cur.scale));
3070    if (of) evas_font_free(obj->layer->evas, of);
3071 }
3072
3073 /**
3074  * @internal
3075  * Returns true if the item is a tab
3076  * @def _IS_TAB(item)
3077  */
3078 #define _IS_TAB(item)                                             \
3079    (!strcmp(item, "tab") || !strcmp(item, "\t") || !strcmp(item, "\\t"))
3080 /**
3081  * @internal
3082  * Returns true if the item is a line spearator, false otherwise
3083  * @def _IS_LINE_SEPARATOR(item)
3084  */
3085 #define _IS_LINE_SEPARATOR(item)                                             \
3086    (!strcmp(item, "br") || !strcmp(item, "\n") || !strcmp(item, "\\n"))
3087 /**
3088  * @internal
3089  * Returns true if the item is a paragraph separator, false otherwise
3090  * @def _IS_PARAGRAPH_SEPARATOR(item)
3091  */
3092 #define _IS_PARAGRAPH_SEPARATOR_SIMPLE(item)                                 \
3093    (!strcmp(item, "ps"))
3094 /**
3095  * @internal
3096  * Returns true if the item is a paragraph separator, false otherwise
3097  * takes legacy mode into account.
3098  * @def _IS_PARAGRAPH_SEPARATOR(item)
3099  */
3100 #define _IS_PARAGRAPH_SEPARATOR(o, item)                                     \
3101    (_IS_PARAGRAPH_SEPARATOR_SIMPLE(item) ||                                  \
3102     (o->legacy_newline && _IS_LINE_SEPARATOR(item))) /* Paragraph separator */
3103
3104 /**
3105  * @internal
3106  * Handles a format by processing a format node. It returns the relevant format
3107  * through _fmt and updates the padding through style_pad_*. If needed,
3108  * it creates a format item.
3109  *
3110  * @param obj the evas object - NOT NULL.
3111  * @param c the current context- NOT NULL.
3112  * @param _fmt the format that holds the result.
3113  * @param n the source format node - not null.
3114  * @param style_pad_l the pad to update.
3115  * @param style_pad_r the pad to update.
3116  * @param style_pad_t the pad to update.
3117  * @param style_pad_b the pad to update.
3118  * @param create_item Create a new format item if true, only process otherwise.
3119  */
3120 static void
3121 _layout_do_format(const Evas_Object *obj __UNUSED__, Ctxt *c,
3122       Evas_Object_Textblock_Format **_fmt, Evas_Object_Textblock_Node_Format *n,
3123       int *style_pad_l, int *style_pad_r, int *style_pad_t, int *style_pad_b,
3124       Eina_Bool create_item)
3125 {
3126    Evas_Object_Textblock_Format *fmt = *_fmt;
3127    /* FIXME: comment the algo */
3128
3129    const char *s;
3130    const char *item;
3131    int handled = 0;
3132
3133    s = n->format;
3134    if (!strncmp(s, "item ", 5))
3135      {
3136         // one of:
3137         //   item size=20x10 href=name
3138         //   item relsize=20x10 href=name
3139         //   item abssize=20x10 href=name
3140         // 
3141         // optional arguments:
3142         //   vsize=full
3143         //   vsize=ascent
3144         // 
3145         // size == item size (modifies line size) - can be multiplied by
3146         //   scale factor
3147         // relsize == relative size (height is current font height, width
3148         //   modified accordingly keeping aspect)
3149         // abssize == absolute size (modifies line size) - never mulitplied by
3150         //   scale factor
3151         // href == name of item - to be found and matched later and used for
3152         //   positioning
3153         Evas_Object_Textblock_Format_Item *fi;
3154         int w = 1, h = 1;
3155         int vsize = 0, size = 0;
3156         char *p;
3157
3158         // don't care
3159         //href = strstr(s, " href=");
3160         p = strstr(s, " vsize=");
3161         if (p)
3162           {
3163              p += 7;
3164              if (!strncmp(p, "full", 4)) vsize = VSIZE_FULL;
3165              else if (!strncmp(p, "ascent", 6)) vsize = VSIZE_ASCENT;
3166           }
3167         p = strstr(s, " size=");
3168         if (p)
3169           {
3170              p += 6;
3171              if (sscanf(p, "%ix%i", &w, &h) == 2)
3172                {
3173                   /* this is handled somewhere else because it depends
3174                    * on the current scaling factor of the object which
3175                    * may change and break because the results of this
3176                    * function are cached */
3177                   size = SIZE;
3178                }
3179           }
3180         else
3181           {
3182              p = strstr(s, " absize=");
3183              if (p)
3184                {
3185                   p += 8;
3186                   if (sscanf(p, "%ix%i", &w, &h) == 2)
3187                     {
3188                        size = SIZE_ABS;
3189                     }
3190                }
3191              else
3192                {
3193                   p = strstr(s, " relsize=");
3194                   if (p)
3195                     {
3196                        /* this is handled somewhere else because it depends
3197                         * on the line it resides in, which is not defined
3198                         * at this point and will change anyway, which will
3199                         * break because the results of this function are
3200                         * cached */
3201                        size = SIZE_REL;
3202                     }
3203                }
3204           }
3205
3206         if (create_item)
3207           {
3208              fi = _layout_format_item_add(c, n, s, fmt);
3209              fi->vsize = vsize;
3210              fi->size = size;
3211              fi->formatme = 1;
3212              /* For formats items it's usually
3213                 the same, we don't handle the
3214                 special cases yet. */
3215              fi->parent.w = fi->parent.adv = w;
3216              fi->parent.h = h;
3217           }
3218         /* Not sure if it's the best handling, but will do it for now. */
3219         fmt = _layout_format_push(c, fmt, n);
3220         handled = 1;
3221      }
3222
3223    if (!handled)
3224      {
3225         Eina_Bool push_fmt = EINA_FALSE;
3226         if (n->opener && !n->own_closer)
3227           {
3228              fmt = _layout_format_push(c, fmt, n);
3229              push_fmt = EINA_TRUE;
3230           }
3231         else if (!n->opener)
3232           {
3233              fmt = _layout_format_pop(c, n->orig_format);
3234           }
3235         while ((item = _format_parse(&s)))
3236           {
3237              if (_format_is_param(item))
3238                {
3239                   /* Only handle it if it's a push format, otherwise,
3240                    * don't let overwrite the format stack.. */
3241                   if (push_fmt)
3242                     {
3243                        _layout_format_value_handle(c, fmt, item);
3244                     }
3245                }
3246              else if (create_item)
3247                {
3248                   if ((_IS_PARAGRAPH_SEPARATOR(c->o, item)) ||
3249                         (_IS_LINE_SEPARATOR(item)))
3250                     {
3251                        Evas_Object_Textblock_Format_Item *fi;
3252
3253                        fi = _layout_format_item_add(c, n, item, fmt);
3254
3255                        fi->parent.w = fi->parent.adv = 0;
3256                     }
3257                   else if (_IS_TAB(item))
3258                     {
3259                        Evas_Object_Textblock_Format_Item *fi;
3260
3261                        fi = _layout_format_item_add(c, n, item, fmt);
3262                        fi->parent.w = fi->parent.adv = fmt->tabstops;
3263                        fi->formatme = 1;
3264                     }
3265                }
3266           }
3267         _format_finalize(c->obj, fmt);
3268      }
3269
3270      {
3271         Evas_Coord pad_l, pad_r, pad_t, pad_b;
3272         pad_l = pad_r = pad_t = pad_b = 0;
3273         evas_text_style_pad_get(fmt->style, &pad_l, &pad_r, &pad_t, &pad_b);
3274         if (pad_l > *style_pad_l) *style_pad_l = pad_l;
3275         if (pad_r > *style_pad_r) *style_pad_r = pad_r;
3276         if (pad_t > *style_pad_t) *style_pad_t = pad_t;
3277         if (pad_b > *style_pad_b) *style_pad_b = pad_b;
3278      }
3279
3280    if (fmt->underline2)
3281      c->have_underline2 = 1;
3282    else if (fmt->underline || fmt->underline_dash)
3283      c->have_underline = 1;
3284    *_fmt = fmt;
3285 }
3286
3287 static void
3288 _layout_update_par(Ctxt *c)
3289 {
3290    Evas_Object_Textblock_Paragraph *last_par;
3291    last_par = (Evas_Object_Textblock_Paragraph *)
3292       EINA_INLIST_GET(c->par)->prev;
3293    if (last_par)
3294      {
3295         c->par->y = last_par->y + last_par->h;
3296      }
3297    else
3298      {
3299         c->par->y = 0;
3300      }
3301 }
3302
3303 /* -1 means no wrap */
3304 static int
3305 _layout_get_charwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3306       const Evas_Object_Textblock_Item *it, size_t line_start,
3307       const char *breaks)
3308 {
3309    int wrap;
3310    size_t uwrap;
3311    size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
3312    /* Currently not being used, because it doesn't contain relevant
3313     * information */
3314    (void) breaks;
3315
3316      {
3317         if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3318            wrap = 0;
3319         else
3320            wrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it));
3321
3322         if (wrap < 0)
3323            return -1;
3324         uwrap = (size_t) wrap + it->text_pos;
3325      }
3326
3327
3328    if ((uwrap == line_start) && (it->type == EVAS_TEXTBLOCK_ITEM_TEXT))
3329      {
3330         uwrap = it->text_pos +
3331            (size_t) evas_common_text_props_cluster_next(
3332                  &_ITEM_TEXT(it)->text_props, wrap);
3333      }
3334    if ((uwrap <= line_start) || (uwrap > len))
3335       return -1;
3336
3337    return uwrap;
3338 }
3339
3340 /* -1 means no wrap */
3341 #ifdef HAVE_LINEBREAK
3342
3343 /* Allow break means: if we can break after the current char */
3344 #define ALLOW_BREAK(i) \
3345    (breaks[i] <= LINEBREAK_ALLOWBREAK)
3346
3347 #else
3348
3349 #define ALLOW_BREAK(i) \
3350    (_is_white(str[i]))
3351
3352 #endif
3353 static int
3354 _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3355       const Evas_Object_Textblock_Item *it, Eina_Bool mixed_wrap,
3356       size_t line_start, const char *breaks)
3357 {
3358    Eina_Bool wrap_after = EINA_FALSE;
3359    size_t wrap;
3360    size_t orig_wrap;
3361    const Eina_Unicode *str = eina_ustrbuf_string_get(
3362          it->text_node->unicode);
3363    int item_start = it->text_pos;
3364    size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
3365 #ifndef HAVE_LINEBREAK
3366    /* Not used without liblinebreak ATM. */
3367    (void) breaks;
3368 #endif
3369
3370      {
3371         int swrap = -1;
3372         if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3373            swrap = 0;
3374         else
3375            swrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it));
3376         /* Avoiding too small textblocks to even contain one char.
3377          * FIXME: This can cause breaking inside ligatures. */
3378
3379         if (swrap < 0)
3380            return -1;
3381
3382         orig_wrap = wrap = swrap + item_start;
3383      }
3384
3385    if (wrap > line_start)
3386      {
3387         /* The wrapping point found is the first char of the next string
3388            the rest works on the last char of the previous string.
3389            If it's a whitespace, then it's ok, and no need to go back
3390            because we'll remove it anyway. */
3391         if (!_is_white(str[wrap]))
3392            MOVE_PREV_UNTIL(line_start, wrap);
3393         /* If there's a breakable point inside the text, scan backwards until
3394          * we find it */
3395         while (wrap > line_start)
3396           {
3397              if (ALLOW_BREAK(wrap))
3398                 break;
3399              wrap--;
3400           }
3401
3402         if ((wrap > line_start) ||
3403               ((wrap == line_start) && (ALLOW_BREAK(wrap)) && (wrap < len)))
3404           {
3405              /* We found a suitable wrapping point, break here. */
3406              MOVE_NEXT_UNTIL(len, wrap);
3407              return wrap;
3408           }
3409         else
3410           {
3411              if (mixed_wrap)
3412                {
3413                   return ((orig_wrap >= line_start) && (orig_wrap < len)) ?
3414                      ((int) orig_wrap) : -1;
3415                }
3416              else
3417                {
3418                   /* Scan forward to find the next wrapping point */
3419                   wrap = orig_wrap;
3420                   wrap_after = EINA_TRUE;
3421                }
3422           }
3423      }
3424
3425    /* If we need to find the position after the cutting point */
3426    if ((wrap == line_start) || (wrap_after))
3427      {
3428         if (mixed_wrap)
3429           {
3430              return _layout_get_charwrap(c, fmt, it,
3431                    line_start, breaks);
3432           }
3433         else
3434           {
3435              while (wrap < len)
3436                {
3437                   if (ALLOW_BREAK(wrap))
3438                      break;
3439                   wrap++;
3440                }
3441
3442
3443              if ((wrap < len) && (wrap > line_start))
3444                {
3445                   MOVE_NEXT_UNTIL(len, wrap);
3446                   return wrap;
3447                }
3448              else
3449                {
3450                   return -1;
3451                }
3452           }
3453      }
3454
3455    return -1;
3456 }
3457
3458 /* -1 means no wrap */
3459 static int
3460 _layout_get_wordwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3461       const Evas_Object_Textblock_Item *it, size_t line_start,
3462       const char *breaks)
3463 {
3464    return _layout_get_word_mixwrap_common(c, fmt, it, EINA_FALSE, line_start,
3465          breaks);
3466 }
3467
3468 /* -1 means no wrap */
3469 static int
3470 _layout_get_mixedwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3471       const Evas_Object_Textblock_Item *it, size_t line_start,
3472       const char *breaks)
3473 {
3474    return _layout_get_word_mixwrap_common(c, fmt, it, EINA_TRUE, line_start,
3475          breaks);
3476 }
3477
3478 /* Should be moved inside _layout_ellipsis_item_new once we fix the hack in
3479  * textblock render */
3480 static const Eina_Unicode _ellip_str[2] = { 0x2026, '\0' };
3481
3482 static Evas_Object_Textblock_Text_Item *
3483 _layout_ellipsis_item_new(Ctxt *c, const Evas_Object_Textblock_Item *cur_it)
3484 {
3485    Evas_Object_Textblock_Text_Item *ellip_ti;
3486    Evas_Script_Type script;
3487    Evas_Font_Instance *script_fi = NULL, *cur_fi;
3488    size_t len = 1; /* The length of _ellip_str */
3489
3490    /* We can free it here, cause there's only one ellipsis item per tb. */
3491    if (c->o->ellip_ti) _item_free(c->obj, NULL, _ITEM(c->o->ellip_ti));
3492    c->o->ellip_ti = ellip_ti = _layout_text_item_new(c,
3493          eina_list_data_get(eina_list_last(c->format_stack)));
3494    ellip_ti->parent.text_node = cur_it->text_node;
3495    ellip_ti->parent.text_pos = cur_it->text_pos;
3496    script = evas_common_language_script_type_get(_ellip_str, len);
3497
3498    evas_common_text_props_bidi_set(&ellip_ti->text_props,
3499          c->par->bidi_props, ellip_ti->parent.text_pos);
3500    evas_common_text_props_script_set (&ellip_ti->text_props, script);
3501
3502    if (ellip_ti->parent.format->font.font)
3503      {
3504         /* It's only 1 char anyway, we don't need the run end. */
3505         (void) c->ENFN->font_run_end_get(c->ENDT,
3506               ellip_ti->parent.format->font.font, &script_fi, &cur_fi,
3507               script, _ellip_str, len);
3508
3509         c->ENFN->font_text_props_info_create(c->ENDT,
3510               cur_fi, _ellip_str, &ellip_ti->text_props,
3511               c->par->bidi_props, ellip_ti->parent.text_pos, len, EVAS_TEXT_PROPS_MODE_SHAPE);
3512      }
3513
3514    _text_item_update_sizes(c, ellip_ti);
3515
3516    if (cur_it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3517      {
3518         ellip_ti->parent.text_pos += _ITEM_TEXT(cur_it)->text_props.text_len
3519            - 1;
3520      }
3521    else
3522      {
3523         ellip_ti->parent.text_pos++;
3524      }
3525
3526    return ellip_ti;
3527 }
3528
3529 /**
3530  * @internel
3531  * Handle ellipsis
3532  */
3533 static inline void
3534 _layout_handle_ellipsis(Ctxt *c, Evas_Object_Textblock_Item *it, Eina_List *i)
3535 {
3536    Evas_Object_Textblock_Text_Item *ellip_ti;
3537    Evas_Object_Textblock_Item *last_it;
3538    Evas_Coord save_cx;
3539    int wrap;
3540    ellip_ti = _layout_ellipsis_item_new(c, it);
3541    last_it = it;
3542
3543    save_cx = c->x;
3544    c->w -= ellip_ti->parent.w;
3545
3546    if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3547      {
3548         Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
3549
3550         wrap = _layout_text_cutoff_get(c, last_it->format, ti);
3551         if ((wrap > 0) && !IS_AT_END(ti, (size_t) wrap))
3552           {
3553              _layout_item_text_split_strip_white(c, ti, i, wrap);
3554           }
3555         else if ((wrap == 0) && (c->ln->items))
3556           {
3557              last_it = _ITEM(EINA_INLIST_GET(c->ln->items)->last);
3558           }
3559      }
3560    else if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3561      {
3562         /* We don't want to add this format item. */
3563         last_it = NULL;
3564      }
3565
3566    c->x = save_cx;
3567    c->w += ellip_ti->parent.w;
3568    /* If we should add this item, do it */
3569    if (last_it == it)
3570      {
3571         c->ln->items = (Evas_Object_Textblock_Item *)
3572            eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3573                  EINA_INLIST_GET(it));
3574         if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3575           {
3576              Evas_Object_Textblock_Format_Item *fi;
3577              fi = _ITEM_FORMAT(it);
3578              fi->y = c->y;
3579           }
3580      }
3581    c->ln->items = (Evas_Object_Textblock_Item *)
3582       eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3583             EINA_INLIST_GET(_ITEM(ellip_ti)));
3584    _layout_line_finalize(c, ellip_ti->parent.format);
3585 }
3586
3587 #ifdef BIDI_SUPPORT
3588 static void
3589 _layout_paragraph_reorder_lines(Evas_Object_Textblock_Paragraph *par)
3590 {
3591    Evas_Object_Textblock_Line *ln;
3592
3593    EINA_INLIST_FOREACH(EINA_INLIST_GET(par->lines), ln)
3594      {
3595         _layout_line_reorder(ln);
3596      }
3597 }
3598 #endif
3599
3600 static void
3601 _layout_paragraph_render(Evas_Object_Textblock *o,
3602                          Evas_Object_Textblock_Paragraph *par)
3603 {
3604    if (par->rendered)
3605       return;
3606    par->rendered = EINA_TRUE;
3607
3608 #ifdef BIDI_SUPPORT
3609    if (par->is_bidi)
3610      {
3611         _layout_update_bidi_props(o, par);
3612         _layout_paragraph_reorder_lines(par);
3613         /* Clear the bidi props because we don't need them anymore. */
3614         if (par->bidi_props)
3615           {
3616              evas_bidi_paragraph_props_unref(par->bidi_props);
3617              par->bidi_props = NULL;
3618           }
3619      }
3620 #else
3621    (void) o;
3622 #endif
3623 }
3624
3625 /* 0 means go ahead, 1 means break without an error, 2 means
3626  * break with an error, should probably clean this a bit (enum/macro)
3627  * FIXME ^ */
3628 static int
3629 _layout_par(Ctxt *c)
3630 {
3631    Evas_Object_Textblock_Item *it;
3632    Eina_List *i;
3633    int ret = 0;
3634    int wrap = -1;
3635    char *line_breaks = NULL;
3636
3637    if (!c->par->logical_items)
3638      return 2;
3639
3640    /* We want to show it. */
3641    c->par->visible = 1;
3642
3643    /* Check if we need to skip this paragraph because it's already layouted
3644     * correctly, and mark handled nodes as dirty. */
3645    c->par->line_no = c->line_no;
3646
3647    if (c->par->text_node)
3648      {
3649         /* Skip this paragraph if width is the same, there is no ellipsis
3650          * and we aren't just calculating. */
3651         if (!c->par->text_node->is_new && !c->par->text_node->dirty &&
3652               !c->width_changed && c->par->lines &&
3653               !c->o->have_ellipsis)
3654           {
3655              Evas_Object_Textblock_Line *ln;
3656              /* Update c->line_no */
3657              ln = (Evas_Object_Textblock_Line *)
3658                 EINA_INLIST_GET(c->par->lines)->last;
3659              if (ln)
3660                 c->line_no = c->par->line_no + ln->line_no + 1;
3661              return 0;
3662           }
3663         c->par->text_node->dirty = EINA_FALSE;
3664         c->par->text_node->is_new = EINA_FALSE;
3665         c->par->rendered = EINA_FALSE;
3666
3667         /* Merge back and clear the paragraph */
3668           {
3669              Eina_List *itr, *itr_next;
3670              Evas_Object_Textblock_Item *ititr, *prev_it = NULL;
3671              _paragraph_clear(c->obj, c->par);
3672              EINA_LIST_FOREACH_SAFE(c->par->logical_items, itr, itr_next, ititr)
3673                {
3674                   if (ititr->merge && prev_it &&
3675                         (prev_it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
3676                         (ititr->type == EVAS_TEXTBLOCK_ITEM_TEXT))
3677                     {
3678                        _layout_item_merge_and_free(c, _ITEM_TEXT(prev_it),
3679                              _ITEM_TEXT(ititr));
3680                        c->par->logical_items =
3681                           eina_list_remove_list(c->par->logical_items, itr);
3682                     }
3683                   else
3684                     {
3685                        prev_it = ititr;
3686                     }
3687                }
3688           }
3689      }
3690
3691    c->y = c->par->y;
3692
3693    it = _ITEM(eina_list_data_get(c->par->logical_items));
3694    _layout_line_new(c, it->format);
3695    /* We walk on our own because we want to be able to add items from
3696     * inside the list and then walk them on the next iteration. */
3697    for (i = c->par->logical_items ; i ; )
3698      {
3699         int adv_line = 0;
3700         int redo_item = 0;
3701         it = _ITEM(eina_list_data_get(i));
3702         /* Skip visually deleted items */
3703         if (it->visually_deleted)
3704           {
3705              i = eina_list_next(i);
3706              continue;
3707           }
3708
3709         if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3710           {
3711              Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
3712              _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
3713                    &c->maxdescent, ti->parent.format);
3714           }
3715         else
3716           {
3717              Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
3718              if (fi->formatme)
3719                {
3720                   /* If there are no text items yet, calc ascent/descent
3721                    * according to the current format. */
3722                   if (c->maxascent + c->maxdescent == 0)
3723                      _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
3724                            &c->maxdescent, it->format);
3725
3726                   _layout_calculate_format_item_size(c->obj, fi, &c->maxascent,
3727                         &c->maxdescent, &fi->y, &fi->parent.w, &fi->parent.h);
3728                   fi->parent.adv = fi->parent.w;
3729                }
3730           }
3731
3732
3733         /* Check if we need to wrap, i.e the text is bigger than the width,
3734            or we already found a wrap point. */
3735         if ((c->w >= 0) &&
3736               (((c->x + it->adv) >
3737                 (c->w - c->o->style_pad.l - c->o->style_pad.r -
3738                  c->marginl - c->marginr)) || (wrap > 0)))
3739           {
3740              /* Handle ellipsis here. If we don't have more width left
3741               * and no height left, or no more width left and no wrapping. */
3742              if ((it->format->ellipsis == 1.0) && (c->h >= 0) &&
3743                    ((2 * it->h + c->y >
3744                      c->h - c->o->style_pad.t - c->o->style_pad.b) ||
3745                     (!it->format->wrap_word && !it->format->wrap_char &&
3746                      !it->format->wrap_mixed)))
3747                {
3748                   _layout_handle_ellipsis(c, it, i);
3749                   ret = 1;
3750                   goto end;
3751                }
3752              /* If we want to wrap and it's worth checking for wrapping
3753               * (i.e there's actually text). */
3754              else if ((it->format->wrap_word || it->format->wrap_char ||
3755                 it->format->wrap_mixed) && it->text_node)
3756                {
3757                   size_t line_start;
3758                   size_t it_len;
3759
3760                   it_len = (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) ?
3761                      1 : _ITEM_TEXT(it)->text_props.text_len;
3762
3763
3764 #ifdef HAVE_LINEBREAK
3765                   /* If we haven't calculated the linebreaks yet,
3766                    * do */
3767                   if (!line_breaks)
3768                     {
3769                        /* Only relevant in those cases */
3770                        if (it->format->wrap_word || it->format->wrap_mixed)
3771                          {
3772                             const char *lang;
3773                             lang = (it->format->font.fdesc) ?
3774                                it->format->font.fdesc->lang : "";
3775                             size_t len =
3776                                eina_ustrbuf_length_get(
3777                                      it->text_node->unicode);
3778                             line_breaks = malloc(len);
3779                             set_linebreaks_utf32((const utf32_t *)
3780                                   eina_ustrbuf_string_get(
3781                                      it->text_node->unicode),
3782                                   len, lang, line_breaks);
3783                          }
3784                     }
3785 #endif
3786                   if (c->ln->items)
3787                      line_start = c->ln->items->text_pos;
3788                   else
3789                      line_start = it->text_pos;
3790
3791                   adv_line = 1;
3792                   /* If we don't already have a wrap point from before */
3793                   if (wrap < 0)
3794                     {
3795                        if (it->format->wrap_word)
3796                           wrap = _layout_get_wordwrap(c, it->format, it,
3797                                 line_start, line_breaks);
3798                        else if (it->format->wrap_char)
3799                           wrap = _layout_get_charwrap(c, it->format, it,
3800                                 line_start, line_breaks);
3801                        else if (it->format->wrap_mixed)
3802                           wrap = _layout_get_mixedwrap(c, it->format, it,
3803                                 line_start, line_breaks);
3804                        else
3805                           wrap = -1;
3806                     }
3807
3808                   /* If it's before the item, rollback and apply.
3809                      if it's in the item, cut.
3810                      If it's after the item, delay the cut */
3811                   if (wrap > 0)
3812                     {
3813                        size_t uwrap = (size_t) wrap;
3814                        if (uwrap < it->text_pos)
3815                          {
3816                             /* Rollback latest additions, and cut that
3817                                item */
3818                             i = eina_list_prev(i);
3819                             it = eina_list_data_get(i);
3820                             while (uwrap < it->text_pos)
3821                               {
3822                                  c->ln->items = _ITEM(
3823                                        eina_inlist_remove(
3824                                           EINA_INLIST_GET(c->ln->items),
3825                                           EINA_INLIST_GET(it)));
3826                                  i = eina_list_prev(i);
3827                                  it = eina_list_data_get(i);
3828                               }
3829                             c->x = it->x;
3830                             c->ln->items = _ITEM(
3831                                   eina_inlist_remove(
3832                                      EINA_INLIST_GET(c->ln->items),
3833                                      EINA_INLIST_GET(it)));
3834                             continue;
3835                          }
3836                        /* If it points to the end, it means the previous
3837                         * char is a whitespace we should remove, so this
3838                         * is a wanted cutting point. */
3839                        else if (uwrap > it->text_pos + it_len)
3840                          {
3841                             /* FIXME: Should redo the ellipsis handling.
3842                              * If we can do ellipsis, just cut here. */
3843                             if (it->format->ellipsis == 1.0)
3844                               {
3845                                  _layout_handle_ellipsis(c, it, i);
3846                                  ret = 1;
3847                                  goto end;
3848                               }
3849                             else
3850                               {
3851                                  /* Delay the cut in a smart way i.e use the
3852                                     item_pos as the line_start, because
3853                                     there's already no cut before*/
3854                                  wrap = -1;
3855                               }
3856                         }
3857                        else
3858                           wrap -= it->text_pos; /* Cut here */
3859                     }
3860
3861                   if (wrap > 0)
3862                     {
3863                        if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3864                          {
3865                             _layout_item_text_split_strip_white(c,
3866                                   _ITEM_TEXT(it), i, wrap);
3867                          }
3868                     }
3869                   else if (wrap == 0)
3870                     {
3871                        /* Should wrap before the item */
3872                        adv_line = 0;
3873                        redo_item = 1;
3874                        _layout_line_advance(c, it->format);
3875                     }
3876                   /* Reset wrap */
3877                   wrap = -1;
3878                }
3879           }
3880
3881         if (!redo_item && !it->visually_deleted)
3882           {
3883              c->ln->items = (Evas_Object_Textblock_Item *)
3884                 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3885                       EINA_INLIST_GET(it));
3886              if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3887                {
3888                   Evas_Object_Textblock_Format_Item *fi;
3889                   fi = _ITEM_FORMAT(it);
3890                   fi->y = c->y;
3891                   /* If it's a newline, and we are not in newline compat
3892                    * mode, or we are in newline compat mode, and this is
3893                    * not used as a paragraph separator, advance */
3894                   if (fi->item && _IS_LINE_SEPARATOR(fi->item) &&
3895                         (!c->o->legacy_newline ||
3896                          eina_list_next(i)))
3897                     {
3898                        adv_line = 1;
3899                     }
3900                }
3901              c->x += it->adv;
3902              i = eina_list_next(i);
3903           }
3904         if (adv_line)
3905           {
3906              /* Each line is according to the first item in it, and here
3907               * i is already the next item (or the current if we redo it) */
3908              if (i)
3909                {
3910                   it = _ITEM(eina_list_data_get(i));
3911                }
3912              _layout_line_advance(c, it->format);
3913           }
3914      }
3915    if (c->ln->items)
3916      {
3917         /* Here 'it' is the last format used */
3918         _layout_line_finalize(c, it->format);
3919      }
3920
3921 end:
3922 #ifdef HAVE_LINEBREAK
3923    if (line_breaks)
3924       free(line_breaks);
3925 #endif
3926
3927    return ret;
3928 }
3929
3930 /**
3931  * @internal
3932  * Invalidate text nodes according to format changes
3933  * This goes through all the new format changes and marks the text nodes
3934  * that should be invalidated because of format changes.
3935  *
3936  * @param c the working context.
3937  */
3938 static inline void
3939 _format_changes_invalidate_text_nodes(Ctxt *c)
3940 {
3941    Evas_Object_Textblock_Node_Format *fnode = c->o->format_nodes;
3942    Evas_Object_Textblock_Node_Text *start_n = NULL;
3943    Eina_List *fstack = NULL;
3944    int balance = 0;
3945    while (fnode)
3946      {
3947         if (fnode->is_new)
3948           {
3949              const char *fstr = fnode->orig_format;
3950              /* balance < 0 means we gave up and everything should be
3951               * invalidated */
3952              if (fnode->opener && !fnode->own_closer)
3953                {
3954                   balance++;
3955                   if (!fstack)
3956                      start_n = fnode->text_node;
3957                   fstack = eina_list_prepend(fstack, fnode);
3958                }
3959              else if (!fnode->opener)
3960                {
3961                   size_t fstr_len;
3962                   fstr_len = strlen(fstr);
3963                   /* Generic popper, just pop */
3964                   if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
3965                     {
3966                        fstack = eina_list_remove_list(fstack, fstack);
3967                        balance--;
3968                     }
3969                   /* Find the matching format and pop it, if the matching format
3970                    * is out format, i.e the last one, pop and break. */
3971                   else
3972                     {
3973                        Eina_List *i;
3974                        Evas_Object_Textblock_Node_Format *fnode2;
3975                        EINA_LIST_FOREACH(fstack, i, fnode2)
3976                          {
3977                             if (_FORMAT_IS_CLOSER_OF(
3978                                      fnode2->orig_format, fstr, fstr_len))
3979                               {
3980                                  fstack = eina_list_remove_list(fstack, i);
3981                                  break;
3982                               }
3983                          }
3984                        balance--;
3985                     }
3986
3987                   if (!fstack)
3988                     {
3989                        Evas_Object_Textblock_Node_Text *f_tnode =
3990                           fnode->text_node;
3991                        while (start_n)
3992                          {
3993                             start_n->dirty = EINA_TRUE;
3994                             if (start_n == f_tnode)
3995                                break;
3996                             start_n =
3997                                _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
3998                          }
3999                        start_n = NULL;
4000                     }
4001                }
4002              else if (!fnode->visible)
4003                 balance = -1;
4004
4005              if (balance < 0)
4006                {
4007                   /* if we don't already have a starting point, use the
4008                    * current paragraph. */
4009                   if (!start_n)
4010                      start_n = fnode->text_node;
4011                   break;
4012                }
4013           }
4014         fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
4015      }
4016
4017    if (balance != 0)
4018      {
4019         while (start_n)
4020           {
4021              start_n->dirty = EINA_TRUE;
4022              start_n = _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
4023           }
4024      }
4025 }
4026
4027
4028 /** FIXME: Document */
4029 static void
4030 _layout_pre(Ctxt *c, int *style_pad_l, int *style_pad_r, int *style_pad_t,
4031       int *style_pad_b)
4032 {
4033    Evas_Object *obj = c->obj;
4034    Evas_Object_Textblock *o = c->o;
4035    /* Mark text nodes as dirty if format have changed. */
4036    if (c->o->format_changed)
4037      {
4038         _format_changes_invalidate_text_nodes(c);
4039      }
4040
4041    if (o->content_changed)
4042      {
4043         Evas_Object_Textblock_Node_Text *n;
4044         c->o->have_ellipsis = 0;
4045         c->par = c->paragraphs = o->paragraphs;
4046         /* Go through all the text nodes to create the logical layout */
4047         EINA_INLIST_FOREACH(c->o->text_nodes, n)
4048           {
4049              Evas_Object_Textblock_Node_Format *fnode;
4050              size_t start;
4051              int off;
4052
4053              /* If it's not a new paragraph, either update it or skip it.
4054               * Remove all the paragraphs that were deleted */
4055              if (!n->is_new)
4056                {
4057                   /* Remove all the deleted paragraphs at this point */
4058                   while (c->par->text_node != n)
4059                     {
4060                        Evas_Object_Textblock_Paragraph *tmp_par =
4061                           (Evas_Object_Textblock_Paragraph *)
4062                           EINA_INLIST_GET(c->par)->next;
4063
4064                        c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4065                           eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
4066                                 EINA_INLIST_GET(c->par));
4067                        _paragraph_free(obj, c->par);
4068
4069                        c->par = tmp_par;
4070                     }
4071
4072                   /* If it's dirty, remove and recreate, if it's clean,
4073                    * skip to the next. */
4074                   if (n->dirty)
4075                     {
4076                        Evas_Object_Textblock_Paragraph *prev_par = c->par;
4077
4078                        _layout_paragraph_new(c, n, EINA_TRUE);
4079
4080                        c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4081                           eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
4082                                 EINA_INLIST_GET(prev_par));
4083                        _paragraph_free(obj, prev_par);
4084                     }
4085                   else
4086                     {
4087                        c->par = (Evas_Object_Textblock_Paragraph *)
4088                           EINA_INLIST_GET(c->par)->next;
4089
4090                        /* Update the format stack according to the node's
4091                         * formats */
4092                        fnode = n->format_node;
4093                        while (fnode && (fnode->text_node == n))
4094                          {
4095                             /* Only do this if this actually changes format */
4096                             if (fnode->format_change)
4097                                _layout_do_format(obj, c, &c->fmt, fnode,
4098                                      style_pad_l, style_pad_r,
4099                                      style_pad_t, style_pad_b, EINA_FALSE);
4100                             fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
4101                          }
4102                        continue;
4103                     }
4104                }
4105              else
4106                {
4107                   /* If it's a new paragraph, just add it. */
4108                   _layout_paragraph_new(c, n, EINA_FALSE);
4109                }
4110
4111 #ifdef BIDI_SUPPORT
4112              _layout_update_bidi_props(c->o, c->par);
4113 #endif
4114
4115              /* For each text node to thorugh all of it's format nodes
4116               * append text from the start to the offset of the next format
4117               * using the last format got. if needed it also creates format
4118               * items this is the core algorithm of the layout mechanism.
4119               * Skip the unicode replacement chars when there are because
4120               * we don't want to print them. */
4121              fnode = n->format_node;
4122              start = off = 0;
4123              while (fnode && (fnode->text_node == n))
4124                {
4125                   off += fnode->offset;
4126                   /* No need to skip on the first run, or a non-visible one */
4127                   _layout_text_append(c, c->fmt, n, start, off, o->repch);
4128                   _layout_do_format(obj, c, &c->fmt, fnode, style_pad_l,
4129                         style_pad_r, style_pad_t, style_pad_b, EINA_TRUE);
4130                   if ((c->have_underline2) || (c->have_underline))
4131                     {
4132                        if (*style_pad_b < c->underline_extend)
4133                          *style_pad_b = c->underline_extend;
4134                        c->have_underline = 0;
4135                        c->have_underline2 = 0;
4136                        c->underline_extend = 0;
4137                     }
4138                   start += off;
4139                   if (fnode->visible)
4140                     {
4141                        off = -1;
4142                        start++;
4143                     }
4144                   else
4145                     {
4146                        off = 0;
4147                     }
4148                   fnode->is_new = EINA_FALSE;
4149                   fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
4150                }
4151              _layout_text_append(c, c->fmt, n, start, -1, o->repch);
4152 #ifdef BIDI_SUPPORT
4153              /* Clear the bidi props because we don't need them anymore. */
4154              if (c->par->bidi_props)
4155                {
4156                   evas_bidi_paragraph_props_unref(c->par->bidi_props);
4157                   c->par->bidi_props = NULL;
4158                }
4159 #endif
4160              c->par = (Evas_Object_Textblock_Paragraph *)
4161                 EINA_INLIST_GET(c->par)->next;
4162           }
4163
4164         /* Delete the rest of the layout paragraphs */
4165         while (c->par)
4166           {
4167              Evas_Object_Textblock_Paragraph *tmp_par =
4168                 (Evas_Object_Textblock_Paragraph *)
4169                 EINA_INLIST_GET(c->par)->next;
4170
4171              c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4172                 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
4173                       EINA_INLIST_GET(c->par));
4174              _paragraph_free(obj, c->par);
4175
4176              c->par = tmp_par;
4177           }
4178         o->paragraphs = c->paragraphs;
4179         c->par = NULL;
4180      }
4181
4182 }
4183
4184 /**
4185  * @internal
4186  * Create the layout from the nodes.
4187  *
4188  * @param obj the evas object - NOT NULL.
4189  * @param calc_only true if should only calc sizes false if should also create the layout.. It assumes native size is being calculated, doesn't support formatted size atm.
4190  * @param w the object's w, -1 means no wrapping (i.e infinite size)
4191  * @param h the object's h, -1 means inifinte size.
4192  * @param w_ret the object's calculated w.
4193  * @param h_ret the object's calculated h.
4194  */
4195 static void
4196 _layout(const Evas_Object *obj, int w, int h, int *w_ret, int *h_ret)
4197 {
4198    Evas_Object_Textblock *o;
4199    Ctxt ctxt, *c;
4200    int style_pad_l = 0, style_pad_r = 0, style_pad_t = 0, style_pad_b = 0;
4201
4202    /* setup context */
4203    o = (Evas_Object_Textblock *)(obj->object_data);
4204    c = &ctxt;
4205    c->obj = (Evas_Object *)obj;
4206    c->o = o;
4207    c->paragraphs = c->par = NULL;
4208    c->format_stack = NULL;
4209    c->fmt = NULL;
4210    c->x = c->y = 0;
4211    c->w = w;
4212    c->h = h;
4213    c->wmax = c->hmax = 0;
4214    c->maxascent = c->maxdescent = 0;
4215    c->marginl = c->marginr = 0;
4216    c->have_underline = 0;
4217    c->have_underline2 = 0;
4218    c->underline_extend = 0;
4219    c->line_no = 0;
4220    c->align = 0.0;
4221    c->align_auto = EINA_TRUE;
4222    c->ln = NULL;
4223    c->width_changed = (obj->cur.geometry.w != o->last_w);
4224
4225    /* Start of logical layout creation */
4226    /* setup default base style */
4227      {
4228         Eina_Bool finalize = EINA_FALSE;
4229         if ((c->o->style) && (c->o->style->default_tag))
4230           {
4231              c->fmt = _layout_format_push(c, NULL, NULL);
4232              _format_fill(c->obj, c->fmt, c->o->style->default_tag);
4233              finalize = EINA_TRUE;
4234           }
4235
4236         if ((c->o->style_user) && (c->o->style_user->default_tag))
4237           {
4238              if (!c->fmt)
4239                {
4240                   c->fmt = _layout_format_push(c, NULL, NULL);
4241                }
4242              _format_fill(c->obj, c->fmt, c->o->style_user->default_tag);
4243              finalize = EINA_TRUE;
4244           }
4245
4246         if (finalize)
4247            _format_finalize(c->obj, c->fmt);
4248      }
4249    if (!c->fmt)
4250      {
4251         if (w_ret) *w_ret = 0;
4252         if (h_ret) *h_ret = 0;
4253         return;
4254      }
4255
4256    _layout_pre(c, &style_pad_l, &style_pad_r, &style_pad_t, &style_pad_b);
4257    c->paragraphs = o->paragraphs;
4258
4259    /* If there are no paragraphs, create the minimum needed,
4260     * if the last paragraph has no lines/text, create that as well */
4261    if (!c->paragraphs)
4262      {
4263         _layout_paragraph_new(c, NULL, EINA_TRUE);
4264         o->paragraphs = c->paragraphs;
4265      }
4266    c->par = (Evas_Object_Textblock_Paragraph *)
4267       EINA_INLIST_GET(c->paragraphs)->last;
4268    if (!c->par->logical_items)
4269      {
4270         Evas_Object_Textblock_Text_Item *ti;
4271         ti = _layout_text_item_new(c, c->fmt);
4272         ti->parent.text_node = c->par->text_node;
4273         ti->parent.text_pos = 0;
4274         _layout_text_add_logical_item(c, ti, NULL);
4275      }
4276
4277    /* End of logical layout creation */
4278
4279    /* Start of visual layout creation */
4280    {
4281       Evas_Object_Textblock_Paragraph *last_vis_par = NULL;
4282       int par_index_step = o->num_paragraphs / TEXTBLOCK_PAR_INDEX_SIZE;
4283       int par_count = 1; /* Force it to take the first one */
4284       int par_index_pos = 0;
4285
4286       if (par_index_step == 0) par_index_step = 1;
4287
4288       /* Clear all of the index */
4289       memset(o->par_index, 0, sizeof(o->par_index));
4290
4291       EINA_INLIST_FOREACH(c->paragraphs, c->par)
4292         {
4293            _layout_update_par(c);
4294
4295            /* Break if we should stop here. */
4296            if (_layout_par(c))
4297              {
4298                 last_vis_par = c->par;
4299                 break;
4300              }
4301
4302            if ((par_index_pos < TEXTBLOCK_PAR_INDEX_SIZE) && (--par_count == 0))
4303              {
4304                 par_count = par_index_step;
4305
4306                 o->par_index[par_index_pos++] = c->par;
4307              }
4308         }
4309
4310       /* Mark all the rest of the paragraphs as invisible */
4311       if (c->par)
4312         {
4313            c->par = (Evas_Object_Textblock_Paragraph *)
4314               EINA_INLIST_GET(c->par)->next;
4315            while (c->par)
4316              {
4317                 c->par->visible = 0;
4318                 c->par = (Evas_Object_Textblock_Paragraph *)
4319                    EINA_INLIST_GET(c->par)->next;
4320              }
4321         }
4322
4323       /* Get the last visible paragraph in the layout */
4324       if (!last_vis_par && c->paragraphs)
4325          last_vis_par = (Evas_Object_Textblock_Paragraph *)
4326             EINA_INLIST_GET(c->paragraphs)->last;
4327
4328       if (last_vis_par)
4329          c->hmax = last_vis_par->y + last_vis_par->h;
4330    }
4331
4332    /* Clean the rest of the format stack */
4333    while (c->format_stack)
4334      {
4335         c->fmt = c->format_stack->data;
4336         c->format_stack = eina_list_remove_list(c->format_stack, c->format_stack);
4337         _format_unref_free(c->obj, c->fmt);
4338      }
4339
4340    if (w_ret) *w_ret = c->wmax;
4341    if (h_ret) *h_ret = c->hmax;
4342
4343    /* Vertically align the textblock */
4344    if ((o->valign > 0.0) && (c->h > c->hmax))
4345      {
4346         Evas_Coord adjustment = (c->h - c->hmax) * o->valign;
4347         Evas_Object_Textblock_Paragraph *par;
4348         EINA_INLIST_FOREACH(c->paragraphs, par)
4349           {
4350              par->y += adjustment;
4351           }
4352      }
4353
4354    if ((o->style_pad.l != style_pad_l) || (o->style_pad.r != style_pad_r) ||
4355        (o->style_pad.t != style_pad_t) || (o->style_pad.b != style_pad_b))
4356      {
4357         o->style_pad.l = style_pad_l;
4358         o->style_pad.r = style_pad_r;
4359         o->style_pad.t = style_pad_t;
4360         o->style_pad.b = style_pad_b;
4361         _paragraphs_clear(obj, c->paragraphs);
4362         _layout(obj, w, h, w_ret, h_ret);
4363      }
4364 }
4365
4366 /*
4367  * @internal
4368  * Relayout the object according to current object size.
4369  *
4370  * @param obj the evas object - NOT NULL.
4371  */
4372 static void
4373 _relayout(const Evas_Object *obj)
4374 {
4375    Evas_Object_Textblock *o;
4376
4377    o = (Evas_Object_Textblock *)(obj->object_data);
4378    _layout(obj, obj->cur.geometry.w, obj->cur.geometry.h,
4379          &o->formatted.w, &o->formatted.h);
4380    o->formatted.valid = 1;
4381    o->last_w = obj->cur.geometry.w;
4382    o->last_h = obj->cur.geometry.h;
4383    o->changed = 0;
4384    o->content_changed = 0;
4385    o->format_changed = EINA_FALSE;
4386    o->redraw = 1;
4387 }
4388
4389 /**
4390  * @internal
4391  * Find the layout item and line that match the text node and position passed.
4392  *
4393  * @param obj the evas object - NOT NULL.
4394  * @param n the text node - Not null.
4395  * @param pos the position to look for - valid.
4396  * @param[out] lnr the line found - not null.
4397  * @param[out] tir the item found - not null.
4398  * @see _find_layout_format_item_line_match()
4399  */
4400 static void
4401 _find_layout_item_line_match(Evas_Object *obj, Evas_Object_Textblock_Node_Text *n, int pos, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Item **itr)
4402 {
4403    Evas_Object_Textblock_Paragraph *found_par;
4404    Evas_Object_Textblock_Line *ln;
4405    Evas_Object_Textblock *o;
4406
4407    o = (Evas_Object_Textblock *)(obj->object_data);
4408    if (!o->formatted.valid) _relayout(obj);
4409
4410    found_par = n->par;
4411    if (found_par)
4412      {
4413         _layout_paragraph_render(o, found_par);
4414         EINA_INLIST_FOREACH(found_par->lines, ln)
4415           {
4416              Evas_Object_Textblock_Item *it;
4417
4418              EINA_INLIST_FOREACH(ln->items, it)
4419                {
4420                   /* FIXME: p should be size_t, same goes for pos */
4421                   int p = (int) it->text_pos;
4422
4423                   if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
4424                     {
4425                        Evas_Object_Textblock_Text_Item *ti =
4426                           _ITEM_TEXT(it);
4427
4428                        p += (int) ti->text_props.text_len;
4429                     }
4430                   else
4431                     {
4432                        p++;
4433                     }
4434
4435                   if (((pos >= (int) it->text_pos) && (pos < p)))
4436                     {
4437                        *lnr = ln;
4438                        *itr = it;
4439                        return;
4440                     }
4441                   else if (p == pos)
4442                     {
4443                        *lnr = ln;
4444                        *itr = it;
4445                     }
4446                }
4447           }
4448      }
4449 }
4450
4451 /**
4452  * @internal
4453  * Return the line number 'line'.
4454  *
4455  * @param obj the evas object - NOT NULL.
4456  * @param line the line to find
4457  * @return the line of line number or NULL if no line found.
4458  */
4459 static Evas_Object_Textblock_Line *
4460 _find_layout_line_num(const Evas_Object *obj, int line)
4461 {
4462    Evas_Object_Textblock_Paragraph *par;
4463    Evas_Object_Textblock_Line *ln;
4464    Evas_Object_Textblock *o;
4465
4466    o = (Evas_Object_Textblock *)(obj->object_data);
4467
4468    par = _layout_find_paragraph_by_line_no(o, line);
4469    if (par)
4470      {
4471         _layout_paragraph_render(o, par);
4472         EINA_INLIST_FOREACH(par->lines, ln)
4473           {
4474              if (par->line_no + ln->line_no == line) return ln;
4475           }
4476      }
4477    return NULL;
4478 }
4479
4480 EAPI Evas_Object *
4481 evas_object_textblock_add(Evas *e)
4482 {
4483    Evas_Object *obj;
4484
4485    MAGIC_CHECK(e, Evas, MAGIC_EVAS);
4486    return NULL;
4487    MAGIC_CHECK_END();
4488    obj = evas_object_new(e);
4489    evas_object_textblock_init(obj);
4490    evas_object_inject(obj, e);
4491    return obj;
4492 }
4493
4494 EAPI Evas_Textblock_Style *
4495 evas_textblock_style_new(void)
4496 {
4497    Evas_Textblock_Style *ts;
4498
4499    ts = calloc(1, sizeof(Evas_Textblock_Style));
4500    return ts;
4501 }
4502
4503 EAPI void
4504 evas_textblock_style_free(Evas_Textblock_Style *ts)
4505 {
4506    if (!ts) return;
4507    if (ts->objects)
4508      {
4509         ts->delete_me = 1;
4510         return;
4511      }
4512    _style_clear(ts);
4513    free(ts);
4514 }
4515
4516 EAPI void
4517 evas_textblock_style_set(Evas_Textblock_Style *ts, const char *text)
4518 {
4519    Eina_List *l;
4520    Evas_Object *obj;
4521
4522    if (!ts) return;
4523    /* If the style wasn't really changed, abort. */
4524    if ((!ts->style_text && !text) ||
4525        (ts->style_text && text && !strcmp(text, ts->style_text)))
4526       return;
4527
4528    EINA_LIST_FOREACH(ts->objects, l, obj)
4529      {
4530         Evas_Object_Textblock *o;
4531
4532         o = (Evas_Object_Textblock *)(obj->object_data);
4533         _evas_textblock_invalidate_all(o);
4534         _evas_textblock_changed(o, obj);
4535      }
4536
4537    _style_replace(ts, text);
4538
4539    if (ts->style_text)
4540      {
4541         // format MUST be KEY='VALUE'[KEY='VALUE']...
4542         const char *p;
4543         const char *key_start, *key_stop, *val_start;
4544
4545         key_start = key_stop = val_start = NULL;
4546         p = ts->style_text;
4547         while (*p)
4548           {
4549              if (!key_start)
4550                {
4551                  if (!isspace((unsigned char)(*p)))
4552                     key_start = p;
4553                }
4554              else if (!key_stop)
4555                {
4556                  if ((*p == '=') || (isspace((unsigned char)(*p))))
4557                     key_stop = p;
4558                }
4559              else if (!val_start)
4560                {
4561                   if (((*p) == '\'') && (*(p + 1)))
4562                     {
4563                        val_start = ++p;
4564                     }
4565                }
4566              if ((key_start) && (key_stop) && (val_start))
4567                {
4568                   char *tags, *replaces = NULL;
4569                   Evas_Object_Style_Tag *tag;
4570                   const char *val_stop = NULL;
4571                   size_t tag_len;
4572                   size_t replace_len;
4573
4574                     {
4575                        Eina_Strbuf *buf = eina_strbuf_new();
4576                        val_stop = val_start;
4577                        while(*p)
4578                          {
4579                             if (*p == '\'')
4580                               {
4581                                  /* Break if we found the tag end */
4582                                  if (p[-1] != '\\')
4583                                    {
4584                                       eina_strbuf_append_length(buf, val_stop,
4585                                             p - val_stop);
4586                                       break;
4587                                    }
4588                                  else
4589                                    {
4590                                       eina_strbuf_append_length(buf, val_stop,
4591                                             p - val_stop - 1);
4592                                       eina_strbuf_append_char(buf, '\'');
4593                                       val_stop = p + 1;
4594                                    }
4595                               }
4596                             p++;
4597                          }
4598                        replaces = eina_strbuf_string_steal(buf);
4599                        eina_strbuf_free(buf);
4600                     }
4601                   /* If we didn't find an end, just aboart. */
4602                   if (!*p)
4603                     {
4604                        if (replaces) free(replaces);
4605                        break;
4606                     }
4607
4608                   tag_len = key_stop - key_start;
4609                   replace_len = val_stop - val_start;
4610
4611                   tags = malloc(tag_len + 1);
4612                   if (tags)
4613                     {
4614                        memcpy(tags, key_start, tag_len);
4615                        tags[tag_len] = 0;
4616                     }
4617
4618                   if ((tags) && (replaces))
4619                     {
4620                        if (!strcmp(tags, "DEFAULT"))
4621                          {
4622                             ts->default_tag = replaces;
4623                             free(tags);
4624                          }
4625                        else
4626                          {
4627                             tag = calloc(1, sizeof(Evas_Object_Style_Tag));
4628                             if (tag)
4629                               {
4630                                  tag->tag.tag = tags;
4631                                  tag->tag.replace = replaces;
4632                                  tag->tag.tag_len = tag_len;
4633                                  tag->tag.replace_len = replace_len;
4634                                  ts->tags = (Evas_Object_Style_Tag *)eina_inlist_append(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
4635                               }
4636                             else
4637                               {
4638                                  free(tags);
4639                                  free(replaces);
4640                               }
4641                          }
4642                     }
4643                   else
4644                     {
4645                        if (tags) free(tags);
4646                        if (replaces) free(replaces);
4647                     }
4648                   key_start = key_stop = val_start = NULL;
4649                }
4650              p++;
4651           }
4652      }
4653 }
4654
4655 EAPI const char *
4656 evas_textblock_style_get(const Evas_Textblock_Style *ts)
4657 {
4658    if (!ts) return NULL;
4659    return ts->style_text;
4660 }
4661
4662 /* textblock styles */
4663
4664 static void
4665 _textblock_style_generic_set(Evas_Object *obj, Evas_Textblock_Style *ts,
4666       Evas_Textblock_Style **obj_ts)
4667 {
4668    TB_HEAD();
4669    if (ts == *obj_ts) return;
4670    if ((ts) && (ts->delete_me)) return;
4671    if (*obj_ts)
4672      {
4673         Evas_Textblock_Style *old_ts;
4674         if (o->markup_text)
4675           {
4676              free(o->markup_text);
4677              o->markup_text = NULL;
4678           }
4679
4680         old_ts = *obj_ts;
4681         old_ts->objects = eina_list_remove(old_ts->objects, obj);
4682         if ((old_ts->delete_me) && (!old_ts->objects))
4683           evas_textblock_style_free(old_ts);
4684      }
4685    if (ts)
4686      {
4687         ts->objects = eina_list_append(ts->objects, obj);
4688      }
4689    *obj_ts = ts;
4690
4691    _evas_textblock_invalidate_all(o);
4692    _evas_textblock_changed(o, obj);
4693 }
4694
4695 EAPI void
4696 evas_object_textblock_style_set(Evas_Object *obj, Evas_Textblock_Style *ts)
4697 {
4698    TB_HEAD();
4699    _textblock_style_generic_set(obj, ts, &(o->style));
4700 }
4701
4702 EAPI const Evas_Textblock_Style *
4703 evas_object_textblock_style_get(const Evas_Object *obj)
4704 {
4705    TB_HEAD_RETURN(NULL);
4706    return o->style;
4707 }
4708
4709 EAPI void
4710 evas_object_textblock_style_user_push(Evas_Object *obj, Evas_Textblock_Style *ts)
4711 {
4712    TB_HEAD();
4713    _textblock_style_generic_set(obj, ts, &(o->style_user));
4714 }
4715
4716 EAPI const Evas_Textblock_Style *
4717 evas_object_textblock_style_user_peek(const Evas_Object *obj)
4718 {
4719    TB_HEAD_RETURN(NULL);
4720    return o->style_user;
4721 }
4722
4723 EAPI void
4724 evas_object_textblock_style_user_pop(Evas_Object *obj)
4725 {
4726    TB_HEAD();
4727    _textblock_style_generic_set(obj, NULL,  &(o->style_user));
4728 }
4729
4730 EAPI void
4731 evas_object_textblock_replace_char_set(Evas_Object *obj, const char *ch)
4732 {
4733    TB_HEAD();
4734    if (o->repch) eina_stringshare_del(o->repch);
4735    if (ch) o->repch = eina_stringshare_add(ch);
4736    else o->repch = NULL;
4737    _evas_textblock_invalidate_all(o);
4738    _evas_textblock_changed(o, obj);
4739 }
4740
4741 EAPI void
4742 evas_object_textblock_legacy_newline_set(Evas_Object *obj, Eina_Bool mode)
4743 {
4744    TB_HEAD();
4745    if (o->legacy_newline == mode)
4746       return;
4747
4748    o->legacy_newline = mode;
4749    /* FIXME: Should recreate all the textnodes... For now, it's just
4750     * for new text inserted. */
4751 }
4752
4753 EAPI Eina_Bool
4754 evas_object_textblock_legacy_newline_get(const Evas_Object *obj)
4755 {
4756    TB_HEAD_RETURN(EINA_FALSE);
4757    return o->legacy_newline;
4758 }
4759
4760 EAPI void
4761 evas_object_textblock_valign_set(Evas_Object *obj, double align)
4762 {
4763    TB_HEAD();
4764    if (align < 0.0) align = 0.0;
4765    else if (align > 1.0) align = 1.0;
4766    if (o->valign == align) return;
4767    o->valign = align;
4768    _evas_textblock_changed(o, obj);
4769 }
4770
4771 EAPI double
4772 evas_object_textblock_valign_get(const Evas_Object *obj)
4773 {
4774    TB_HEAD_RETURN(0.0);
4775    return o->valign;
4776 }
4777
4778 EAPI void
4779 evas_object_textblock_bidi_delimiters_set(Evas_Object *obj, const char *delim)
4780 {
4781    TB_HEAD();
4782    eina_stringshare_replace(&o->bidi_delimiters, delim);
4783 }
4784
4785 EAPI const char *
4786 evas_object_textblock_bidi_delimiters_get(const Evas_Object *obj)
4787 {
4788    TB_HEAD_RETURN(NULL);
4789    return o->bidi_delimiters;
4790 }
4791
4792 EAPI const char *
4793 evas_object_textblock_replace_char_get(Evas_Object *obj)
4794 {
4795    TB_HEAD_RETURN(NULL);
4796    return o->repch;
4797 }
4798
4799 /**
4800  * @internal
4801  * Advance p_buff to point after the end of the string. It's used with the
4802  * @ref escaped_strings[] variable.
4803  *
4804  * @param p_buff the pointer to the current string.
4805  */
4806 static inline void
4807 _escaped_advance_after_end_of_string(const char **p_buf)
4808 {
4809    while (**p_buf != 0) (*p_buf)++;
4810    (*p_buf)++;
4811 }
4812
4813 /**
4814  * @internal
4815  * Advance p_buff to point after the end of the string. It's used with the
4816  * @ref escaped_strings[] variable. Also chec if matches.
4817  * FIXME: doc.
4818  *
4819  * @param p_buff the pointer to the current string.
4820  */
4821 static inline int
4822 _escaped_is_eq_and_advance(const char *s, const char *s_end,
4823       const char **p_m, const char *m_end)
4824 {
4825    Eina_Bool reached_end;
4826    for (;((s < s_end) && (*p_m < m_end)); s++, (*p_m)++)
4827      {
4828         if (*s != **p_m)
4829           {
4830              _escaped_advance_after_end_of_string(p_m);
4831              return 0;
4832           }
4833      }
4834
4835    reached_end = !**p_m;
4836    if (*p_m < m_end)
4837      _escaped_advance_after_end_of_string(p_m);
4838
4839    return ((s == s_end) && reached_end);
4840 }
4841
4842 /**
4843  * @internal
4844  *
4845  * @param s the string to match
4846  */
4847 static inline const char *
4848 _escaped_char_match(const char *s, int *adv)
4849 {
4850    const char *map_itr, *map_end, *mc, *sc;
4851
4852    map_itr = escape_strings;
4853    map_end = map_itr + sizeof(escape_strings);
4854
4855    while (map_itr < map_end)
4856      {
4857         const char *escape;
4858         int match;
4859
4860         escape = map_itr;
4861         _escaped_advance_after_end_of_string(&map_itr);
4862         if (map_itr >= map_end) break;
4863
4864         mc = map_itr;
4865         sc = s;
4866         match = 1;
4867         while ((*mc) && (*sc))
4868           {
4869              if ((unsigned char)*sc < (unsigned char)*mc) return NULL;
4870              if (*sc != *mc)
4871                {
4872                   match = 0;
4873                   break;
4874                }
4875              mc++;
4876              sc++;
4877           }
4878         if (match)
4879           {
4880              *adv = mc - map_itr;
4881              return escape;
4882           }
4883         _escaped_advance_after_end_of_string(&map_itr);
4884      }
4885    return NULL;
4886 }
4887
4888 /**
4889  * @internal
4890  * FIXME: TBD.
4891  *
4892  * @param s the string to match
4893  */
4894 static inline const char *
4895 _escaped_char_get(const char *s, const char *s_end)
4896 {
4897    /* Handle numeric escape codes. */
4898    if (s[1] == '#')
4899      {
4900         static char utf8_escape[7]; /* Support up to 6 bytes utf8 */
4901         char ustr[10];
4902         Eina_Unicode uchar[2] = { 0, 0 };
4903         char *utf8_char;
4904         size_t len = 0;
4905         int base = 10;
4906         s += 2; /* Skip "&#" */
4907
4908         if (tolower((unsigned char)(*s)) == 'x')
4909           {
4910              s++;
4911              base = 16;
4912           }
4913
4914         len = s_end - s;
4915         if (len >= sizeof(ustr) + 1)
4916            len = sizeof(ustr);
4917
4918         memcpy(ustr, s, len);
4919         ustr[len] = '\0';
4920         uchar[0] = strtol(ustr, NULL, base);
4921
4922         if (uchar[0] == 0)
4923           return NULL;
4924
4925         utf8_char = eina_unicode_unicode_to_utf8(uchar, NULL);
4926         strcpy(utf8_escape, utf8_char);
4927         free(utf8_char);
4928
4929         return utf8_escape;
4930      }
4931    else
4932      {
4933         const char *map_itr, *map_end;
4934
4935         map_itr = escape_strings;
4936         map_end = map_itr + sizeof(escape_strings);
4937
4938         while (map_itr < map_end)
4939           {
4940              if (_escaped_is_eq_and_advance(s, s_end, &map_itr, map_end))
4941                 return map_itr;
4942              if (map_itr < map_end)
4943                 _escaped_advance_after_end_of_string(&map_itr);
4944           }
4945      }
4946
4947    return NULL;
4948 }
4949
4950 EAPI const char *
4951 evas_textblock_escape_string_get(const char *escape)
4952 {
4953    /* &amp; -> & */
4954    if (!escape) return NULL;
4955    return _escaped_char_get(escape, escape + strlen(escape));
4956 }
4957
4958 EAPI const char *
4959 evas_textblock_escape_string_range_get(const char *escape_start, const char *escape_end)
4960 {
4961    if ((!escape_start) || (!escape_end)) return NULL;
4962    return _escaped_char_get(escape_start, escape_end);
4963 }
4964
4965 EAPI const char *
4966 evas_textblock_string_escape_get(const char *string, int *len_ret)
4967 {
4968    if ((!string) || (!len_ret)) return NULL;
4969    /* & -> &amp; */
4970    return _escaped_char_match(string, len_ret);
4971 }
4972
4973 /**
4974  * @internal
4975  * Appends the escaped char beteewn s and s_end to the curosr
4976  *
4977  *
4978  * @param s the start of the string
4979  * @param s_end the end of the string.
4980  */
4981 static inline void
4982 _append_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
4983       const char *s_end)
4984 {
4985    const char *escape;
4986
4987    escape = _escaped_char_get(s, s_end);
4988    if (escape)
4989      evas_textblock_cursor_text_append(cur, escape);
4990 }
4991
4992 /**
4993  * @internal
4994  * prepends the escaped char beteewn s and s_end to the curosr
4995  *
4996  *
4997  * @param s the start of the string
4998  * @param s_end the end of the string.
4999  */
5000 static inline void
5001 _prepend_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
5002       const char *s_end)
5003 {
5004    const char *escape;
5005
5006    escape = _escaped_char_get(s, s_end);
5007    if (escape)
5008      evas_textblock_cursor_text_prepend(cur, escape);
5009 }
5010
5011
5012 EAPI void
5013 evas_object_textblock_text_markup_set(Evas_Object *obj, const char *text)
5014 {
5015    TB_HEAD();
5016    if ((text != o->markup_text) && (o->markup_text))
5017      {
5018         free(o->markup_text);
5019         o->markup_text = NULL;
5020      }
5021    _nodes_clear(obj);
5022    if (!o->style && !o->style_user)
5023      {
5024         if (text != o->markup_text)
5025           {
5026              if (text) o->markup_text = strdup(text);
5027           }
5028         return;
5029      }
5030
5031    evas_textblock_cursor_paragraph_first(o->cursor);
5032    evas_textblock_cursor_text_append(o->cursor, "");
5033
5034    evas_object_textblock_text_markup_prepend(o->cursor, text);
5035    /* Point all the cursors to the starrt */
5036      {
5037         Eina_List *l;
5038         Evas_Textblock_Cursor *data;
5039
5040         evas_textblock_cursor_paragraph_first(o->cursor);
5041         EINA_LIST_FOREACH(o->cursors, l, data)
5042            evas_textblock_cursor_paragraph_first(data);
5043      }
5044 }
5045
5046 EAPI void
5047 evas_object_textblock_text_markup_prepend(Evas_Textblock_Cursor *cur, const char *text)
5048 {
5049    if (!cur) return;
5050    Evas_Object *obj = cur->obj;
5051    TB_HEAD();
5052    if (text)
5053      {
5054         char *s, *p;
5055         char *tag_start, *tag_end, *esc_start, *esc_end;
5056
5057         tag_start = tag_end = esc_start = esc_end = NULL;
5058         p = (char *)text;
5059         s = p;
5060         /* This loop goes through all of the mark up text until it finds format
5061          * tags, escape sequences or the terminating NULL. When it finds either
5062          * of those, it appends the text found up until that point to the textblock
5063          * proccesses whatever found. It repeats itself until the termainating
5064          * NULL is reached. */
5065         for (;;)
5066           {
5067              size_t text_len;
5068              /* If we got to the end of string or just finished/started tag
5069               * or escape sequence handling. */
5070              if ((*p == 0) ||
5071                    (tag_end) || (esc_end) ||
5072                    (tag_start) || (esc_start))
5073                {
5074                   if (tag_end)
5075                     {
5076                        /* If we reached to a tag ending, analyze the tag */
5077                        char *ttag;
5078                        size_t ttag_len = tag_end - tag_start;
5079
5080
5081                        ttag = malloc(ttag_len + 1);
5082                        if (ttag)
5083                          {
5084                             memcpy(ttag, tag_start, ttag_len);
5085                             ttag[ttag_len] = 0;
5086                             evas_textblock_cursor_format_prepend(cur, ttag);
5087                             free(ttag);
5088                          }
5089                        tag_start = tag_end = NULL;
5090                     }
5091                   else if (esc_end)
5092                     {
5093                        _prepend_escaped_char(cur, esc_start, esc_end + 1);
5094                        esc_start = esc_end = NULL;
5095                     }
5096                   else if (*p == 0)
5097                     {
5098                        _prepend_text_run(cur, s, p);
5099                        s = NULL;
5100                     }
5101                   if (*p == 0)
5102                     break;
5103                }
5104              if (*p == '<')
5105                {
5106                   if (!esc_start)
5107                     {
5108                        /* Append the text prior to this to the textblock and mark
5109                         * the start of the tag */
5110                        tag_start = p;
5111                        tag_end = NULL;
5112                        _prepend_text_run(cur, s, p);
5113                        s = NULL;
5114                     }
5115                }
5116              else if (*p == '>')
5117                {
5118                   if (tag_start)
5119                     {
5120                        tag_end = p + 1;
5121                        s = p + 1;
5122                     }
5123                }
5124              else if (*p == '&')
5125                {
5126                   if (!tag_start)
5127                     {
5128                        /* Append the text prior to this to the textblock and mark
5129                         * the start of the escape sequence */
5130                        esc_start = p;
5131                        esc_end = NULL;
5132                        _prepend_text_run(cur, s, p);
5133                        s = NULL;
5134                     }
5135                }
5136              else if (*p == ';')
5137                {
5138                   if (esc_start)
5139                     {
5140                        esc_end = p;
5141                        s = p + 1;
5142                     }
5143                }
5144              /* Unicode object replcament char */
5145              else if (!strncmp(_REPLACEMENT_CHAR_UTF8, p,
5146                       text_len = strlen(_REPLACEMENT_CHAR_UTF8)) ||
5147                    !strncmp(_NEWLINE_UTF8, p,
5148                       text_len = strlen(_NEWLINE_UTF8)) ||
5149                    !strncmp(_TAB_UTF8, p,
5150                       text_len = strlen(_TAB_UTF8)) ||
5151                    !strncmp(_PARAGRAPH_SEPARATOR_UTF8, p,
5152                       text_len = strlen(_PARAGRAPH_SEPARATOR_UTF8)))
5153                {
5154                   /*FIXME: currently just remove them, maybe do something
5155                    * fancier in the future, atm it breaks if this char
5156                    * is inside <> */
5157                   _prepend_text_run(cur, s, p);
5158                   /* it's also advanced later in this loop need +text_len
5159                      in total*/
5160                   p += text_len - 1;
5161                   s = p + 1; /* One after the end of the replacement char */
5162                }
5163              p++;
5164           }
5165      }
5166    _evas_textblock_changed(o, obj);
5167 }
5168
5169
5170 /**
5171  * @internal
5172  * An helper function to markup get. Appends the format from fnode to the strbugf txt.
5173  *
5174  * @param o the textblock object.
5175  * @param txt the strbuf to append to.
5176  * @param fnode the format node to process.
5177  */
5178 static void
5179 _markup_get_format_append(Eina_Strbuf *txt, Evas_Object_Textblock_Node_Format *fnode)
5180 {
5181    eina_strbuf_append_char(txt, '<');
5182      {
5183         const char *s;
5184
5185         // FIXME: need to escape
5186         s = fnode->orig_format;
5187         if (!fnode->opener && !fnode->own_closer)
5188            eina_strbuf_append_char(txt, '/');
5189         eina_strbuf_append(txt, s);
5190         if (fnode->own_closer)
5191            eina_strbuf_append_char(txt, '/');
5192      }
5193    eina_strbuf_append_char(txt, '>');
5194 }
5195
5196 /**
5197  * @internal
5198  * An helper function to markup get. Appends the text in text.
5199  *
5200  * @param txt the strbuf to append to.
5201  * @param text the text to process.
5202  */
5203 static void
5204 _markup_get_text_append(Eina_Strbuf *txt, const Eina_Unicode *text)
5205 {
5206    char *p = eina_unicode_unicode_to_utf8(text, NULL);
5207    char *base = p;
5208    while (*p)
5209      {
5210         const char *escape;
5211         int adv;
5212
5213         escape = _escaped_char_match(p, &adv);
5214         if (escape)
5215           {
5216              p += adv;
5217              eina_strbuf_append(txt, escape);
5218           }
5219         else
5220           {
5221              eina_strbuf_append_char(txt, *p);
5222              p++;
5223           }
5224      }
5225    free(base);
5226 }
5227 EAPI const char *
5228 evas_object_textblock_text_markup_get(const Evas_Object *obj)
5229 {
5230    Evas_Object_Textblock_Node_Text *n;
5231    Eina_Strbuf *txt = NULL;
5232
5233    TB_HEAD_RETURN(NULL);
5234    if (o->markup_text) return(o->markup_text);
5235    txt = eina_strbuf_new();
5236    EINA_INLIST_FOREACH(o->text_nodes, n)
5237      {
5238         Evas_Object_Textblock_Node_Format *fnode;
5239         Eina_Unicode *text_base, *text;
5240         int off;
5241         int len;
5242
5243         /* For each text node to thorugh all of it's format nodes
5244          * append text from the start to the offset of the next format
5245          * using the last format got. if needed it also creates format items
5246          * this is the core algorithm of the layout mechanism.
5247          * Skip the unicode replacement chars when there are because
5248          * we don't want to print them. */
5249         len = (int) eina_ustrbuf_length_get(n->unicode);
5250         text_base = text =
5251            eina_unicode_strndup(eina_ustrbuf_string_get(n->unicode), len);
5252         fnode = n->format_node;
5253         off = 0;
5254         while (fnode && (fnode->text_node == n))
5255           {
5256              Eina_Unicode tmp_ch;
5257              off += fnode->offset;
5258              
5259              if (off > len) break;
5260              /* No need to skip on the first run */
5261              tmp_ch = text[off];
5262              text[off] = 0; /* Null terminate the part of the string */
5263              _markup_get_text_append(txt, text);
5264              _markup_get_format_append(txt, fnode);
5265              text[off] = tmp_ch; /* Restore the char */
5266              text += off;
5267              if (fnode->visible)
5268                {
5269                   off = -1;
5270                   text++;
5271                }
5272              else
5273                {
5274                   off = 0;
5275                }
5276              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
5277           }
5278         /* Add the rest, skip replacement */
5279         _markup_get_text_append(txt, text);
5280         free(text_base);
5281      }
5282
5283
5284    o->markup_text = eina_strbuf_string_steal(txt);
5285    eina_strbuf_free(txt);
5286    return o->markup_text;
5287 }
5288
5289 EAPI char *
5290 evas_textblock_text_markup_to_utf8(const Evas_Object *obj, const char *text)
5291 {
5292    /* FIXME: Redundant and awful, should be merged with markup_prepend */
5293    Eina_Strbuf *sbuf;
5294    char *s, *p, *ret;
5295    char *tag_start, *tag_end, *esc_start, *esc_end;
5296
5297    if (!text) return NULL;
5298
5299
5300    tag_start = tag_end = esc_start = esc_end = NULL;
5301    sbuf = eina_strbuf_new();
5302    p = (char *)text;
5303    s = p;
5304    /* This loop goes through all of the mark up text until it finds format
5305     * tags, escape sequences or the terminating NULL. When it finds either
5306     * of those, it appends the text found up until that point to the textblock
5307     * proccesses whatever found. It repeats itself until the termainating
5308     * NULL is reached. */
5309    for (;;)
5310      {
5311         /* If we got to the end of string or just finished/started tag
5312          * or escape sequence handling. */
5313         if ((*p == 0) ||
5314               (tag_end) || (esc_end) ||
5315               (tag_start) || (esc_start))
5316           {
5317              if (tag_end)
5318                {
5319                   /* If we reached to a tag ending, analyze the tag */
5320                   char *ttag;
5321                   size_t ttag_len;
5322
5323                   tag_start++; /* Skip the < */
5324                   tag_end--; /* Skip the > */
5325                   if ((tag_end > tag_start) && (*(tag_end - 1) == '/'))
5326                     {
5327                        tag_end --; /* Skip the terminating '/' */
5328                        while (*(tag_end - 1) == ' ')
5329                          tag_end--; /* skip trailing ' ' */
5330                     }
5331
5332                   ttag_len = tag_end - tag_start;
5333
5334                   ttag = malloc(ttag_len + 1);
5335                   if (ttag)
5336                     {
5337                        const char *match = NULL;
5338                        size_t replace_len;
5339                        memcpy(ttag, tag_start, ttag_len);
5340                        ttag[ttag_len] = 0;
5341
5342
5343                        if (obj)
5344                          {
5345                             match = _style_match_tag(
5346                                   evas_object_textblock_style_get(obj),
5347                                   ttag, ttag_len, &replace_len);
5348                          }
5349
5350                        if (!match) match = ttag;
5351
5352                        if (_IS_PARAGRAPH_SEPARATOR_SIMPLE(match))
5353                           eina_strbuf_append(sbuf, _PARAGRAPH_SEPARATOR_UTF8);
5354                        else if (_IS_LINE_SEPARATOR(match))
5355                           eina_strbuf_append(sbuf, _NEWLINE_UTF8);
5356                        else if (_IS_TAB(match))
5357                           eina_strbuf_append(sbuf, _TAB_UTF8);
5358                        else if (!strncmp(match, "item", 4))
5359                           eina_strbuf_append(sbuf, _REPLACEMENT_CHAR_UTF8);
5360
5361                        free(ttag);
5362                     }
5363                   tag_start = tag_end = NULL;
5364                }
5365              else if (esc_end)
5366                {
5367                   const char *escape;
5368
5369                   escape = _escaped_char_get(esc_start, esc_end + 1);
5370                   if (escape) eina_strbuf_append(sbuf, escape);
5371                   esc_start = esc_end = NULL;
5372                }
5373              else if (*p == 0)
5374                {
5375                   eina_strbuf_append_length(sbuf, s, p - s);
5376                   s = NULL;
5377                }
5378              if (*p == 0)
5379                 break;
5380           }
5381         if (*p == '<')
5382           {
5383              if (!esc_start)
5384                {
5385                   /* Append the text prior to this to the textblock and
5386                    * mark the start of the tag */
5387                   tag_start = p;
5388                   tag_end = NULL;
5389                   eina_strbuf_append_length(sbuf, s, p - s);
5390                   s = NULL;
5391                }
5392           }
5393         else if (*p == '>')
5394           {
5395              if (tag_start)
5396                {
5397                   tag_end = p + 1;
5398                   s = p + 1;
5399                }
5400           }
5401         else if (*p == '&')
5402           {
5403              if (!tag_start)
5404                {
5405                   /* Append the text prior to this to the textblock and mark
5406                    * the start of the escape sequence */
5407                   esc_start = p;
5408                   esc_end = NULL;
5409                   eina_strbuf_append_length(sbuf, s, p - s);
5410                   s = NULL;
5411                }
5412           }
5413         else if (*p == ';')
5414           {
5415              if (esc_start)
5416                {
5417                   esc_end = p;
5418                   s = p + 1;
5419                }
5420           }
5421         p++;
5422      }
5423
5424    ret = eina_strbuf_string_steal(sbuf);
5425    eina_strbuf_free(sbuf);
5426    return ret;
5427 }
5428
5429 EAPI char *
5430 evas_textblock_text_utf8_to_markup(const Evas_Object *obj, const char *text)
5431 {
5432    Eina_Strbuf *sbuf;
5433    char *str = NULL;
5434    int ch, pos = 0, pos2 = 0;
5435
5436    (void) obj;
5437
5438    if (!text) return NULL;
5439
5440    sbuf = eina_strbuf_new();
5441
5442    for (;;)
5443      {
5444         pos = pos2;
5445         pos2 = evas_string_char_next_get(text, pos2, &ch);
5446         if ((ch <= 0) || (pos2 <= 0)) break;
5447
5448         if (ch == _NEWLINE)
5449            eina_strbuf_append(sbuf, "<br/>");
5450         else if (ch == _TAB)
5451            eina_strbuf_append(sbuf, "<tab/>");
5452         else if (ch == '<')
5453            eina_strbuf_append(sbuf, "&lt;");
5454         else if (ch == '>')
5455            eina_strbuf_append(sbuf, "&gt;");
5456         else if (ch == '&')
5457            eina_strbuf_append(sbuf, "&amp;");
5458         else if (ch == _PARAGRAPH_SEPARATOR)
5459            eina_strbuf_append(sbuf, "<ps/>");
5460         else if (ch == _REPLACEMENT_CHAR)
5461            eina_strbuf_append(sbuf, "&#xfffc;");
5462         else
5463           {
5464              eina_strbuf_append_length(sbuf, text + pos, pos2 - pos);
5465           }
5466      }
5467    str = eina_strbuf_string_steal(sbuf);
5468    eina_strbuf_free(sbuf);
5469    return str;
5470
5471 }
5472
5473 /* cursors */
5474
5475 /**
5476  * @internal
5477  * Merge the current node with the next, no need to remove PS, already
5478  * not there.
5479  *
5480  * @param o the text block object.
5481  * @param to merge into to.
5482  */
5483 static void
5484 _evas_textblock_nodes_merge(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *to)
5485 {
5486    Evas_Object_Textblock_Node_Format *itr;
5487    Evas_Object_Textblock_Node_Format *pnode;
5488    Evas_Object_Textblock_Node_Text *from;
5489    const Eina_Unicode *text;
5490    int to_len, len;
5491
5492    if (!to) return;
5493    from = _NODE_TEXT(EINA_INLIST_GET(to)->next);
5494
5495    to_len = eina_ustrbuf_length_get(to->unicode);
5496    text = eina_ustrbuf_string_get(from->unicode);
5497    len = eina_ustrbuf_length_get(from->unicode);
5498    eina_ustrbuf_append_length(to->unicode, text, len);
5499
5500    itr = from->format_node;
5501    if (itr && (itr->text_node == from))
5502      {
5503         pnode = _NODE_FORMAT(EINA_INLIST_GET(itr)->prev);
5504         if (pnode && (pnode->text_node == to))
5505           {
5506              itr->offset += to_len - _evas_textblock_node_format_pos_get(pnode);
5507           }
5508         else
5509           {
5510              itr->offset += to_len;
5511           }
5512      }
5513
5514    while (itr && (itr->text_node == from))
5515      {
5516         itr->text_node = to;
5517         itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
5518      }
5519    if (!to->format_node || (to->format_node->text_node != to))
5520      {
5521         to->format_node = from->format_node;
5522      }
5523
5524    /* When it comes to how we handle it, merging is like removing both nodes
5525     * and creating a new one, se we need to do the needed cleanups. */
5526    if (to->par)
5527       to->par->text_node = NULL;
5528    to->par = NULL;
5529
5530    to->is_new = EINA_TRUE;
5531
5532    _evas_textblock_cursors_set_node(o, from, to);
5533    _evas_textblock_node_text_remove(o, from);
5534 }
5535
5536 /**
5537  * @internal
5538  * Merge the current node with the next, no need to remove PS, already
5539  * not there.
5540  *
5541  * @param cur the cursor that points to the current node
5542  */
5543 static void
5544 _evas_textblock_cursor_nodes_merge(Evas_Textblock_Cursor *cur)
5545 {
5546    Evas_Object_Textblock_Node_Text *nnode;
5547    Evas_Object_Textblock *o;
5548    int len;
5549    if (!cur) return;
5550
5551    len = eina_ustrbuf_length_get(cur->node->unicode);
5552
5553    o = (Evas_Object_Textblock *)(cur->obj->object_data);
5554    nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
5555    _evas_textblock_nodes_merge(o, cur->node);
5556    _evas_textblock_cursors_update_offset(cur, nnode, 0, len);
5557    _evas_textblock_cursors_set_node(o, nnode, cur->node);
5558    if (nnode == o->cursor->node)
5559      {
5560         o->cursor->node = cur->node;
5561         o->cursor->pos += len;
5562      }
5563 }
5564
5565 /**
5566  * @internal
5567  * Return the format at a specific position.
5568  *
5569  * @param cur the cursor to the position.
5570  * @return the format node at the specific position or NULL if not found.
5571  */
5572 static Evas_Object_Textblock_Node_Format *
5573 _evas_textblock_cursor_node_format_at_pos_get(const Evas_Textblock_Cursor *cur)
5574 {
5575    Evas_Object_Textblock_Node_Format *node;
5576    Evas_Object_Textblock_Node_Format *itr;
5577    int position = 0;
5578
5579    TB_NULL_CHECK(cur->node, NULL);
5580
5581    node = cur->node->format_node;
5582    if (!node) return NULL;
5583    /* If there is no exclusive format node to this paragraph return the
5584     * previous's node */
5585    /* Find the main format node */
5586    EINA_INLIST_FOREACH(node, itr)
5587      {
5588         if (itr->text_node != cur->node)
5589           {
5590              return NULL;
5591           }
5592         if ((position + itr->offset) == cur->pos)
5593           {
5594              return itr;
5595           }
5596         position += itr->offset;
5597      }
5598    return NULL;
5599 }
5600
5601 /**
5602  * @internal
5603  * Return the last format node at the position of the format node n.
5604  *
5605  * @param n a format node at the position.
5606  * @return the last format node at the position of n.
5607  */
5608 static Evas_Object_Textblock_Node_Format *
5609 _evas_textblock_node_format_last_at_off(const Evas_Object_Textblock_Node_Format *n)
5610 {
5611    const Evas_Object_Textblock_Node_Format *nnode;
5612    const Evas_Object_Textblock_Node_Text *tnode;
5613    if (!n) return NULL;
5614    nnode = n;
5615    tnode = n->text_node;
5616    do
5617      {
5618         n = nnode;
5619         nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
5620      }
5621    while (nnode && (nnode->text_node == tnode) && (nnode->offset == 0));
5622
5623    return (Evas_Object_Textblock_Node_Format *) n;
5624 }
5625
5626 /**
5627  * @internal
5628  * Returns the visible format at a specific location.
5629  *
5630  * @param n a format at the specific position.
5631  * @return the format node at the specific position or NULL if not found.
5632  */
5633 static Evas_Object_Textblock_Node_Format *
5634 _evas_textblock_node_visible_at_pos_get(const Evas_Object_Textblock_Node_Format *n)
5635 {
5636    const Evas_Object_Textblock_Node_Format *nnode;
5637    if (!n) return NULL;
5638    /* The visible format is the last one, because it inserts a replacement
5639     * char that advances the next formats. */
5640
5641    nnode = n;
5642    do
5643      {
5644         n = nnode;
5645         if (n->visible) return (Evas_Object_Textblock_Node_Format *) n;
5646         nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
5647      }
5648    while (nnode && (nnode->offset == 0));
5649
5650    return NULL;
5651 }
5652
5653 /**
5654  * @internal
5655  * Return the last format that applies to a specific cursor or at the specific
5656  * position the cursor points to. This means either a cursor at or before the
5657  * position of the cursor in the text node is returned or the previous's text
5658  * node's format node.
5659  *
5660  * @param cur the position to look at.
5661  * @return the format node found.
5662  */
5663 static Evas_Object_Textblock_Node_Format *
5664 _evas_textblock_cursor_node_format_before_or_at_pos_get(const Evas_Textblock_Cursor *cur)
5665 {
5666    Evas_Object_Textblock_Node_Format *node, *pitr = NULL;
5667    Evas_Object_Textblock_Node_Format *itr;
5668    size_t position = 0;
5669
5670    TB_NULL_CHECK(cur->node, NULL);
5671
5672    node = cur->node->format_node;
5673    if (!node) return NULL;
5674    /* If there is no exclusive format node to this paragraph return the
5675     * previous's node */
5676    if (node->text_node != cur->node)
5677      {
5678         return node;
5679      }
5680    else if (node->offset > cur->pos)
5681      {
5682         return _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5683      }
5684    /* Find the main format node */
5685    pitr = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5686    EINA_INLIST_FOREACH(node, itr)
5687      {
5688         if ((itr->text_node != cur->node) ||
5689             ((position + itr->offset) > cur->pos))
5690           {
5691              return pitr;
5692           }
5693         else if ((position + itr->offset) == cur->pos)
5694           {
5695              return itr;
5696           }
5697         pitr = itr;
5698         position += itr->offset;
5699      }
5700    return pitr;
5701 }
5702
5703 /**
5704  * @internal
5705  * Find the layout item and line that match the cursor.
5706  *
5707  * @param cur the cursor we are currently at. - NOT NULL.
5708  * @param[out] lnr the line found - not null.
5709  * @param[out] itr the item found - not null.
5710  * @return @c EINA_TRUE if we matched the previous format, @c EINA_FALSE
5711  * otherwise.
5712  */
5713 static Eina_Bool
5714 _find_layout_item_match(const Evas_Textblock_Cursor *cur, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Item **itr)
5715 {
5716    Evas_Textblock_Cursor cur2;
5717    Eina_Bool previous_format = EINA_FALSE;
5718
5719    cur2.obj = cur->obj;
5720    evas_textblock_cursor_copy(cur, &cur2);
5721    if (cur2.pos > 0)
5722      {
5723         cur2.pos--;
5724      }
5725
5726    if (_evas_textblock_cursor_is_at_the_end(cur) &&
5727             evas_textblock_cursor_format_is_visible_get(&cur2))
5728      {
5729         _find_layout_item_line_match(cur2.obj, cur2.node, cur2.pos, lnr, itr);
5730         previous_format = EINA_TRUE;
5731      }
5732    else
5733      {
5734         _find_layout_item_line_match(cur->obj, cur->node, cur->pos, lnr, itr);
5735      }
5736    return previous_format;
5737 }
5738
5739 EAPI Evas_Textblock_Cursor *
5740 evas_object_textblock_cursor_get(const Evas_Object *obj)
5741 {
5742    TB_HEAD_RETURN(NULL);
5743    return o->cursor;
5744 }
5745
5746 EAPI Evas_Textblock_Cursor *
5747 evas_object_textblock_cursor_new(const Evas_Object *obj)
5748 {
5749    Evas_Textblock_Cursor *cur;
5750
5751    TB_HEAD_RETURN(NULL);
5752    cur = calloc(1, sizeof(Evas_Textblock_Cursor));
5753    cur->obj = (Evas_Object *) obj;
5754    cur->node = o->text_nodes;
5755    cur->pos = 0;
5756
5757    o->cursors = eina_list_append(o->cursors, cur);
5758    return cur;
5759 }
5760
5761 EAPI void
5762 evas_textblock_cursor_free(Evas_Textblock_Cursor *cur)
5763 {
5764    Evas_Object_Textblock *o;
5765
5766    if (!cur) return;
5767    o = (Evas_Object_Textblock *)(cur->obj->object_data);
5768    if (cur == o->cursor) return;
5769    o->cursors = eina_list_remove(o->cursors, cur);
5770    free(cur);
5771 }
5772
5773 EAPI Eina_Bool
5774 evas_textblock_cursor_is_format(const Evas_Textblock_Cursor *cur)
5775 {
5776    if (!cur || !cur->node) return EINA_FALSE;
5777    return (_evas_textblock_cursor_node_format_at_pos_get(cur)) ?
5778       EINA_TRUE : EINA_FALSE;
5779 }
5780
5781 EAPI const Eina_List *
5782 evas_textblock_node_format_list_get(const Evas_Object *obj, const char *anchor)
5783 {
5784    TB_HEAD_RETURN(NULL);
5785    if (!strcmp(anchor, "a"))
5786       return o->anchors_a;
5787    else if (!strcmp(anchor, "item"))
5788       return o->anchors_item;
5789
5790    return NULL;
5791 }
5792
5793 EAPI const Evas_Object_Textblock_Node_Format *
5794 evas_textblock_node_format_first_get(const Evas_Object *obj)
5795 {
5796    TB_HEAD_RETURN(NULL);
5797    return o->format_nodes;
5798 }
5799
5800 EAPI const Evas_Object_Textblock_Node_Format *
5801 evas_textblock_node_format_last_get(const Evas_Object *obj)
5802 {
5803    TB_HEAD_RETURN(NULL);
5804    if (o->format_nodes)
5805      {
5806         return _NODE_FORMAT(EINA_INLIST_GET(o->format_nodes)->last);
5807      }
5808    return NULL;
5809 }
5810
5811 EAPI const Evas_Object_Textblock_Node_Format *
5812 evas_textblock_node_format_next_get(const Evas_Object_Textblock_Node_Format *n)
5813 {
5814    if (!n) return NULL;
5815    return _NODE_FORMAT(EINA_INLIST_GET(n)->next);
5816 }
5817
5818 EAPI const Evas_Object_Textblock_Node_Format *
5819 evas_textblock_node_format_prev_get(const Evas_Object_Textblock_Node_Format *n)
5820 {
5821    if (!n) return NULL;
5822    return _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
5823 }
5824
5825 EAPI void
5826 evas_textblock_node_format_remove_pair(Evas_Object *obj,
5827       Evas_Object_Textblock_Node_Format *n)
5828 {
5829    Evas_Object_Textblock_Node_Text *tnode1;
5830    Evas_Object_Textblock_Node_Format *fmt, *found_node = NULL;
5831    Eina_List *fstack = NULL;
5832    TB_HEAD();
5833
5834    if (!n) return;
5835
5836    fmt = n;
5837
5838    do
5839      {
5840         const char *fstr = fmt->orig_format;
5841
5842         if (fmt->opener && !fmt->own_closer)
5843           {
5844              fstack = eina_list_prepend(fstack, fmt);
5845           }
5846         else if (fstr && !fmt->opener)
5847           {
5848              size_t fstr_len;
5849              fstr_len = strlen(fstr);
5850              /* Generic popper, just pop */
5851              if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
5852                {
5853                   fstack = eina_list_remove_list(fstack, fstack);
5854                   if (!fstack)
5855                     {
5856                        found_node = fmt;
5857                        goto found;
5858                     }
5859                }
5860              /* Find the matching format and pop it, if the matching format
5861               * is out format, i.e the last one, pop and break. */
5862              else
5863                {
5864                   Eina_List *i;
5865                   Evas_Object_Textblock_Node_Format *fnode;
5866                   EINA_LIST_FOREACH(fstack, i, fnode)
5867                     {
5868                        if (_FORMAT_IS_CLOSER_OF(
5869                                 fnode->orig_format, fstr, fstr_len))
5870                          {
5871                             /* Last one, this is our item! */
5872                             if (!eina_list_next(i))
5873                               {
5874                                  found_node = fmt;
5875                                  goto found;
5876                               }
5877                             fstack = eina_list_remove_list(fstack, i);
5878                             break;
5879                          }
5880                     }
5881                }
5882           }
5883
5884         fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
5885      }
5886    while (fmt && fstack);
5887
5888 found:
5889
5890    fstack = eina_list_free(fstack);
5891
5892    if (n->visible)
5893      {
5894         size_t ind = _evas_textblock_node_format_pos_get(n);
5895         const char *format = n->format;
5896         Evas_Textblock_Cursor cur;
5897         cur.obj = obj;
5898
5899         eina_ustrbuf_remove(n->text_node->unicode, ind, ind + 1);
5900         if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
5901           {
5902              evas_textblock_cursor_at_format_set(&cur, n);
5903              _evas_textblock_cursor_nodes_merge(&cur);
5904           }
5905         _evas_textblock_cursors_update_offset(&cur, n->text_node, ind, -1);
5906      }
5907    tnode1 = n->text_node;
5908    _evas_textblock_node_format_remove(o, n, 0);
5909    if (found_node && (found_node != n))
5910      {
5911         Evas_Object_Textblock_Node_Text *tnode2;
5912         tnode2 = found_node->text_node;
5913         /* found_node can never be visible! (it's the closing format) */
5914         _evas_textblock_node_format_remove(o, found_node, 0);
5915
5916         /* FIXME: Should be unified in the layout, for example, added to a list
5917          * that checks this kind of removals. But until then, this is very fast
5918          * and works. */
5919         /* Mark all the text nodes in between the removed formats as dirty. */
5920         while (tnode1)
5921           {
5922              tnode1->dirty = EINA_TRUE;
5923              if (tnode1 == tnode2)
5924                 break;
5925              tnode1 =
5926                 _NODE_TEXT(EINA_INLIST_GET(tnode1)->next);
5927           }
5928      }
5929
5930    _evas_textblock_changed(o, obj);
5931 }
5932
5933 EAPI void
5934 evas_textblock_cursor_paragraph_first(Evas_Textblock_Cursor *cur)
5935 {
5936    Evas_Object_Textblock *o;
5937    if (!cur) return;
5938    o = (Evas_Object_Textblock *)(cur->obj->object_data);
5939    cur->node = o->text_nodes;
5940    cur->pos = 0;
5941
5942 }
5943
5944 EAPI void
5945 evas_textblock_cursor_paragraph_last(Evas_Textblock_Cursor *cur)
5946 {
5947    Evas_Object_Textblock *o;
5948    Evas_Object_Textblock_Node_Text *node;
5949
5950    if (!cur) return;
5951    o = (Evas_Object_Textblock *)(cur->obj->object_data);
5952    node = o->text_nodes;
5953    if (node)
5954      {
5955         node = _NODE_TEXT(EINA_INLIST_GET(node)->last);
5956         cur->node = node;
5957         cur->pos = 0;
5958
5959         evas_textblock_cursor_paragraph_char_last(cur);
5960      }
5961    else
5962      {
5963         cur->node = NULL;
5964         cur->pos = 0;
5965
5966      }
5967 }
5968
5969 EAPI Eina_Bool
5970 evas_textblock_cursor_paragraph_next(Evas_Textblock_Cursor *cur)
5971 {
5972    if (!cur) return EINA_FALSE;
5973    TB_NULL_CHECK(cur->node, EINA_FALSE);
5974    /* If there is a current text node, return the next text node (if exists)
5975     * otherwise, just return False. */
5976    if (cur->node)
5977      {
5978         Evas_Object_Textblock_Node_Text *nnode;
5979         nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
5980         if (nnode)
5981           {
5982              cur->node = nnode;
5983              cur->pos = 0;
5984
5985              return EINA_TRUE;
5986           }
5987      }
5988    return EINA_FALSE;
5989 }
5990
5991 EAPI Eina_Bool
5992 evas_textblock_cursor_paragraph_prev(Evas_Textblock_Cursor *cur)
5993 {
5994    Evas_Object_Textblock_Node_Text *node;
5995    if (!cur) return EINA_FALSE;
5996    TB_NULL_CHECK(cur->node, EINA_FALSE);
5997    /* If the current node is a text node, just get the prev if any,
5998     * if it's a format, get the current text node out of the format and return
5999     * the prev text node if any. */
6000    node = cur->node;
6001    /* If there is a current text node, return the prev text node
6002     * (if exists) otherwise, just return False. */
6003    if (node)
6004      {
6005         Evas_Object_Textblock_Node_Text *pnode;
6006         pnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->prev);
6007         if (pnode)
6008           {
6009              cur->node = pnode;
6010              evas_textblock_cursor_paragraph_char_last(cur);
6011              return EINA_TRUE;
6012           }
6013      }
6014    return EINA_FALSE;
6015 }
6016
6017 EAPI void
6018 evas_textblock_cursor_set_at_format(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *n)
6019 {
6020    evas_textblock_cursor_at_format_set(cur, n);
6021 }
6022
6023 EAPI Eina_Bool
6024 evas_textblock_cursor_format_next(Evas_Textblock_Cursor *cur)
6025 {
6026    Evas_Object_Textblock_Node_Format *node;
6027
6028    if (!cur) return EINA_FALSE;
6029    TB_NULL_CHECK(cur->node, EINA_FALSE);
6030    /* If the current node is a format node, just get the next if any,
6031     * if it's a text, get the current format node out of the text and return
6032     * the next format node if any. */
6033    node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
6034    node = _evas_textblock_node_format_last_at_off(node);
6035    if (!node)
6036      {
6037         if (cur->node->format_node)
6038           {
6039              cur->pos = _evas_textblock_node_format_pos_get(node);
6040              return EINA_TRUE;
6041           }
6042      }
6043    /* If there is a current text node, return the next format node (if exists)
6044     * otherwise, just return False. */
6045    else
6046      {
6047         Evas_Object_Textblock_Node_Format *nnode;
6048         nnode = _NODE_FORMAT(EINA_INLIST_GET(node)->next);
6049         if (nnode)
6050           {
6051              cur->node = nnode->text_node;
6052              cur->pos = _evas_textblock_node_format_pos_get(nnode);
6053
6054              return EINA_TRUE;
6055           }
6056      }
6057    return EINA_FALSE;
6058 }
6059
6060 EAPI Eina_Bool
6061 evas_textblock_cursor_format_prev(Evas_Textblock_Cursor *cur)
6062 {
6063    const Evas_Object_Textblock_Node_Format *node;
6064    if (!cur) return EINA_FALSE;
6065    TB_NULL_CHECK(cur->node, EINA_FALSE);
6066    node = evas_textblock_cursor_format_get(cur);
6067    if (!node)
6068      {
6069         node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
6070         if (node)
6071           {
6072              cur->node = node->text_node;
6073              cur->pos = _evas_textblock_node_format_pos_get(node);
6074
6075              return EINA_TRUE;
6076           }
6077      }
6078    /* If there is a current text node, return the next text node (if exists)
6079     * otherwise, just return False. */
6080    if (node)
6081      {
6082         Evas_Object_Textblock_Node_Format *pnode;
6083         pnode = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
6084         if (pnode)
6085           {
6086              cur->node = pnode->text_node;
6087              cur->pos = _evas_textblock_node_format_pos_get(pnode);
6088
6089              return EINA_TRUE;
6090           }
6091      }
6092    return EINA_FALSE;
6093 }
6094
6095 #ifdef HAVE_LINEBREAK
6096
6097 /* BREAK_AFTER: true if we can break after the current char.
6098  * Both macros assume str[i] is not the terminating nul */
6099 #define BREAK_AFTER(i) \
6100    (breaks[i] == WORDBREAK_BREAK)
6101
6102 #else
6103
6104 #define BREAK_AFTER(i) \
6105    ((!text[i + 1]) || \
6106     (_is_white(text[i]) && !_is_white(text[i + 1])) || \
6107     (!_is_white(text[i]) && _is_white(text[i + 1])))
6108
6109 #endif
6110
6111 EAPI Eina_Bool
6112 evas_textblock_cursor_word_start(Evas_Textblock_Cursor *cur)
6113 {
6114    const Eina_Unicode *text;
6115    size_t i;
6116 #ifdef HAVE_LINEBREAK
6117    char *breaks;
6118 #endif
6119
6120    if (!cur) return EINA_FALSE;
6121    TB_NULL_CHECK(cur->node, EINA_FALSE);
6122
6123    text = eina_ustrbuf_string_get(cur->node->unicode);
6124
6125 #ifdef HAVE_LINEBREAK
6126      {
6127         const char *lang = ""; /* FIXME: get lang */
6128         size_t len = eina_ustrbuf_length_get(cur->node->unicode);
6129         breaks = malloc(len);
6130         set_wordbreaks_utf32((const utf32_t *) text, len, lang, breaks);
6131      }
6132 #endif
6133
6134    i = cur->pos;
6135
6136    /* Skip the first one. This ensures we don't point to the nul, and also
6137     * we just don't care about it anyway. */
6138    if (i > 0) i--;
6139
6140    for ( ; i > 0 ; i--)
6141      {
6142         if (BREAK_AFTER(i))
6143           {
6144              /* Advance to the current char */
6145              i++;
6146              break;
6147           }
6148      }
6149
6150    cur->pos = i;
6151
6152 #ifdef HAVE_LINEBREAK
6153    free(breaks);
6154 #endif
6155    return EINA_TRUE;
6156 }
6157
6158 EAPI Eina_Bool
6159 evas_textblock_cursor_word_end(Evas_Textblock_Cursor *cur)
6160 {
6161    const Eina_Unicode *text;
6162    size_t i;
6163 #ifdef HAVE_LINEBREAK
6164    char *breaks;
6165 #endif
6166
6167    if (!cur) return EINA_FALSE;
6168    TB_NULL_CHECK(cur->node, EINA_FALSE);
6169
6170    text = eina_ustrbuf_string_get(cur->node->unicode);
6171
6172 #ifdef HAVE_LINEBREAK
6173      {
6174         const char *lang = ""; /* FIXME: get lang */
6175         size_t len = eina_ustrbuf_length_get(cur->node->unicode);
6176         breaks = malloc(len);
6177         set_wordbreaks_utf32((const utf32_t *) text, len, lang, breaks);
6178      }
6179 #endif
6180
6181    i = cur->pos;
6182
6183    for ( ; text[i] ; i++)
6184      {
6185         if (BREAK_AFTER(i))
6186           {
6187              /* This is the one to break after. */
6188              break;
6189           }
6190      }
6191
6192    cur->pos = i;
6193
6194 #ifdef HAVE_LINEBREAK
6195    free(breaks);
6196 #endif
6197    return EINA_TRUE;;
6198 }
6199
6200 EAPI Eina_Bool
6201 evas_textblock_cursor_char_next(Evas_Textblock_Cursor *cur)
6202 {
6203    int ind;
6204    const Eina_Unicode *text;
6205
6206    if (!cur) return EINA_FALSE;
6207    TB_NULL_CHECK(cur->node, EINA_FALSE);
6208
6209    ind = cur->pos;
6210    text = eina_ustrbuf_string_get(cur->node->unicode);
6211    if (text[ind]) ind++;
6212    /* Only allow pointing a null if it's the last paragraph.
6213     * because we don't have a PS there. */
6214    if (text[ind])
6215      {
6216         cur->pos = ind;
6217         return EINA_TRUE;
6218      }
6219    else
6220      {
6221         if (!evas_textblock_cursor_paragraph_next(cur))
6222           {
6223              /* If we already were at the end, that means we don't have
6224               * where to go next we should return FALSE */
6225              if (cur->pos == (size_t) ind)
6226                return EINA_FALSE;
6227
6228              cur->pos = ind;
6229              return EINA_TRUE;
6230           }
6231         else
6232           {
6233              return EINA_TRUE;
6234           }
6235      }
6236 }
6237
6238 EAPI Eina_Bool
6239 evas_textblock_cursor_char_prev(Evas_Textblock_Cursor *cur)
6240 {
6241    if (!cur) return EINA_FALSE;
6242    TB_NULL_CHECK(cur->node, EINA_FALSE);
6243
6244    if (cur->pos != 0)
6245      {
6246         cur->pos--;
6247         return EINA_TRUE;
6248      }
6249    return evas_textblock_cursor_paragraph_prev(cur);
6250 }
6251
6252 EAPI void
6253 evas_textblock_cursor_paragraph_char_first(Evas_Textblock_Cursor *cur)
6254 {
6255    if (!cur) return;
6256    cur->pos = 0;
6257
6258 }
6259
6260 EAPI void
6261 evas_textblock_cursor_paragraph_char_last(Evas_Textblock_Cursor *cur)
6262 {
6263    int ind;
6264
6265    if (!cur) return;
6266    TB_NULL_CHECK(cur->node);
6267    ind = eina_ustrbuf_length_get(cur->node->unicode);
6268    /* If it's not the last paragraph, go back one, because we want to point
6269     * to the PS, not the NULL */
6270    if (EINA_INLIST_GET(cur->node)->next)
6271       ind--;
6272
6273    if (ind >= 0)
6274       cur->pos = ind;
6275    else
6276       cur->pos = 0;
6277
6278 }
6279
6280 EAPI void
6281 evas_textblock_cursor_line_char_first(Evas_Textblock_Cursor *cur)
6282 {
6283    Evas_Object_Textblock *o;
6284    Evas_Object_Textblock_Line *ln = NULL;
6285    Evas_Object_Textblock_Item *it = NULL;
6286
6287    if (!cur) return;
6288    TB_NULL_CHECK(cur->node);
6289    o = (Evas_Object_Textblock *)(cur->obj->object_data);
6290    if (!o->formatted.valid) _relayout(cur->obj);
6291
6292    _find_layout_item_match(cur, &ln, &it);
6293
6294    if (!ln) return;
6295    if (ln->items)
6296      {
6297         Evas_Object_Textblock_Item *i;
6298         it = ln->items;
6299         EINA_INLIST_FOREACH(ln->items, i)
6300           {
6301              if (it->text_pos > i->text_pos)
6302                {
6303                   it = i;
6304                }
6305           }
6306      }
6307    if (it)
6308      {
6309         cur->pos = it->text_pos;
6310         cur->node = it->text_node;
6311      }
6312 }
6313
6314 EAPI void
6315 evas_textblock_cursor_line_char_last(Evas_Textblock_Cursor *cur)
6316 {
6317    Evas_Object_Textblock *o;
6318    Evas_Object_Textblock_Line *ln = NULL;
6319    Evas_Object_Textblock_Item *it = NULL;
6320
6321    if (!cur) return;
6322    TB_NULL_CHECK(cur->node);
6323    o = (Evas_Object_Textblock *)(cur->obj->object_data);
6324    if (!o->formatted.valid) _relayout(cur->obj);
6325
6326    _find_layout_item_match(cur, &ln, &it);
6327
6328    if (!ln) return;
6329    if (ln->items)
6330      {
6331         Evas_Object_Textblock_Item *i;
6332         it = ln->items;
6333         EINA_INLIST_FOREACH(ln->items, i)
6334           {
6335              if (it->text_pos < i->text_pos)
6336                {
6337                   it = i;
6338                }
6339           }
6340      }
6341    if (it)
6342      {
6343         size_t ind;
6344
6345         cur->node = it->text_node;
6346         cur->pos = it->text_pos;
6347         if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
6348           {
6349              ind = _ITEM_TEXT(it)->text_props.text_len - 1;
6350              if (!IS_AT_END(_ITEM_TEXT(it), ind)) ind++;
6351              cur->pos += ind;
6352           }
6353         else if (!EINA_INLIST_GET(ln)->next && !EINA_INLIST_GET(ln->par)->next)
6354           {
6355              cur->pos++;
6356           }
6357      }
6358 }
6359
6360 /**
6361  * @internal
6362  * checks if a format (as a string) is visible/changes format and sets the
6363  * fnode properties accordingly.
6364  *
6365  * @param fnode the format node
6366  * @param s the string.
6367  */
6368 static void
6369 _evas_textblock_format_is_visible(Evas_Object_Textblock_Node_Format *fnode,
6370       const char *s)
6371 {
6372    const char *item;
6373    Eina_Bool is_opener = EINA_TRUE;
6374
6375    fnode->visible = fnode->format_change = EINA_FALSE;
6376    fnode->anchor = ANCHOR_NONE;
6377    if (!s) return;
6378
6379    if (!fnode->own_closer)
6380      {
6381         is_opener = fnode->opener;
6382         fnode->format_change = EINA_TRUE;
6383      }
6384
6385    while ((item = _format_parse(&s)))
6386      {
6387         int itlen = s - item;
6388         /* We care about all of the formats even after a - except for
6389          * item which we don't care after a - because it's just a standard
6390          * closing */
6391         if ((!strncmp(item, "\n", itlen) || !strncmp(item, "\\n", itlen)) ||
6392               (!strncmp(item, "\t", itlen) || !strncmp(item, "\\t", itlen)) ||
6393               (!strncmp(item, "br", itlen) && (itlen >= 2)) ||
6394               (!strncmp(item, "tab", itlen) && (itlen >= 3)) ||
6395               (!strncmp(item, "ps", itlen) && (itlen >= 2)) ||
6396               (!strncmp(item, "item", itlen) && (itlen >= 4) && is_opener))
6397           {
6398              fnode->visible = EINA_TRUE;
6399           }
6400
6401         if (is_opener && !strncmp(item, "a", itlen))
6402           {
6403              fnode->anchor = ANCHOR_A;
6404           }
6405         else if (is_opener && !strncmp(item, "item", itlen) && (itlen >= 4))
6406           {
6407              fnode->anchor = ANCHOR_ITEM;
6408           }
6409      }
6410 }
6411
6412 /**
6413  * Sets the cursor to the position of where the fmt points to.
6414  *
6415  * @param cur the cursor to update.
6416  * @param fmt the format to set according to.
6417  * @return nothing.
6418  */
6419 static void __UNUSED__
6420 _evas_textblock_cursor_node_text_at_format(Evas_Textblock_Cursor *cur, Evas_Object_Textblock_Node_Format *fmt)
6421 {
6422    Evas_Object_Textblock_Node_Text *text;
6423    Evas_Object_Textblock_Node_Format *base_format;
6424    Evas_Object_Textblock_Node_Format *itr;
6425    size_t position = 0;
6426
6427    if (!cur || !fmt) return;
6428    /* Find the main format node */
6429    text = fmt->text_node;
6430    cur->node = text;
6431    base_format = text->format_node;
6432    EINA_INLIST_FOREACH(base_format, itr)
6433      {
6434         if (itr == fmt)
6435           {
6436              break;
6437           }
6438         position += itr->offset;
6439      }
6440    cur->pos = position;
6441
6442 }
6443
6444
6445 /**
6446  * @internal
6447  * Remove pairs of + and - formats and also remove formats without + or -
6448  * i.e formats that pair to themselves. Only removes invisible formats
6449  * that pair themselves, if you want to remove invisible formats that pair
6450  * themselves, please first change fmt->visible to EINA_FALSE.
6451  *
6452  * @param o the textblock object.
6453  * @param fmt the current format.
6454  */
6455 static void
6456 _evas_textblock_node_format_remove_matching(Evas_Object_Textblock *o,
6457       Evas_Object_Textblock_Node_Format *fmt)
6458 {
6459    Evas_Object_Textblock_Node_Text *tnode;
6460    Eina_List *formats = NULL;
6461    size_t offset = 0;
6462
6463    if (!fmt) return;
6464
6465    tnode = fmt->text_node;
6466
6467    do
6468      {
6469         Evas_Object_Textblock_Node_Format *nnode;
6470         const char *fstr = fmt->orig_format;
6471
6472         nnode = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
6473         if (nnode)
6474           {
6475              offset = nnode->offset;
6476           }
6477
6478
6479         if (fmt->opener && !fmt->own_closer)
6480           {
6481              formats = eina_list_prepend(formats, fmt);
6482           }
6483         else if (fstr && !fmt->opener)
6484           {
6485              Evas_Object_Textblock_Node_Format *fnode;
6486              size_t fstr_len;
6487              fstr_len = strlen(fstr);
6488              /* Generic popper, just pop (if there's anything to pop). */
6489              if (formats && (((fstr[0] == ' ') && !fstr[1]) || !fstr[0]))
6490                {
6491                   fnode = eina_list_data_get(formats);
6492                   formats = eina_list_remove_list(formats, formats);
6493                   _evas_textblock_node_format_remove(o, fnode, 0);
6494                   _evas_textblock_node_format_remove(o, fmt, 0);
6495                }
6496              /* Find the matching format and pop it, if the matching format
6497               * is our format, i.e the last one, pop and break. */
6498              else
6499                {
6500                   Eina_List *i, *next;
6501                   EINA_LIST_FOREACH_SAFE(formats, i, next, fnode)
6502                     {
6503                        if (_FORMAT_IS_CLOSER_OF(
6504                                 fnode->orig_format, fstr, fstr_len))
6505                          {
6506                             fnode = eina_list_data_get(i);
6507                             formats = eina_list_remove_list(formats, i);
6508                             _evas_textblock_node_format_remove(o, fnode, 0);
6509                             _evas_textblock_node_format_remove(o, fmt, 0);
6510                             break;
6511                          }
6512                     }
6513                }
6514           }
6515         else if (!fmt->visible)
6516           {
6517              _evas_textblock_node_format_remove(o, fmt, 0);
6518           }
6519         fmt = nnode;
6520      }
6521    while (fmt && (offset == 0) && (fmt->text_node == tnode));
6522    eina_list_free(formats);
6523 }
6524 /**
6525  * @internal
6526  * Add the offset (may be negative) to the first node after fmt which is
6527  * pointing to the text node tnode or to o->format_nodes if fmt is null
6528  * and it points to tnode.
6529  *
6530  * @param o the textblock object.
6531  * @param tnode the text node the format should point to.
6532  * @param fmt the current format.
6533  * @param offset the offest to add (may be negative).
6534  */
6535 static void
6536 _evas_textblock_node_format_adjust_offset(Evas_Object_Textblock *o,
6537       Evas_Object_Textblock_Node_Text *tnode,
6538       Evas_Object_Textblock_Node_Format *fmt, int offset)
6539 {
6540    if (fmt)
6541      {
6542         fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
6543      }
6544    else
6545      {
6546         fmt = o->format_nodes;
6547      }
6548    if (fmt && (tnode == fmt->text_node))
6549      {
6550         fmt->offset += offset;
6551      }
6552 }
6553
6554 /**
6555  * @internal
6556  * Removes a format node updating the offset of the next format node and the
6557  * text nodes pointing to this node.
6558  *
6559  * @param o the textblock object.
6560  * @param n the fromat node to remove
6561  */
6562 static void
6563 _evas_textblock_node_format_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n, int visible_adjustment)
6564 {
6565    /* Update the text nodes about the change */
6566      {
6567         Evas_Object_Textblock_Node_Format *nnode;
6568         nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->next);
6569         /* If there's a next node that belongs to the same text node
6570          * and the curret node was the main one, advance the format node */
6571         if (nnode && (nnode->text_node == n->text_node))
6572           {
6573              if (nnode->text_node->format_node == n)
6574                {
6575                   nnode->text_node->format_node = nnode;
6576                }
6577           }
6578         else
6579           {
6580              Evas_Object_Textblock_Node_Text *tnode;
6581              /* If there's no next one update the text nodes */
6582              nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
6583              tnode = n->text_node;
6584              /* Even if it's not the current text_node's main node
6585               * it can still be the next's. */
6586              if (tnode && (tnode->format_node != n))
6587                {
6588                   tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
6589                }
6590              while (tnode && (tnode->format_node == n))
6591                {
6592                   tnode->format_node = nnode;
6593                   tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
6594                }
6595           }
6596      }
6597    _evas_textblock_node_format_adjust_offset(o, n->text_node, n,
6598          n->offset - visible_adjustment);
6599
6600    o->format_nodes = _NODE_FORMAT(eina_inlist_remove(
6601            EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n)));
6602    _evas_textblock_node_format_free(o, n);
6603 }
6604
6605 /**
6606  * @internal
6607  * Sets all the offsets of the format nodes between start and end in the text
6608  * node n to 0 and sets visibility to EINA_FALSE.
6609  * If end == -1 end means the end of the string.
6610  * Assumes there is a prev node or the current node will be preserved.
6611  *
6612  * @param n the text node the positinos refer to.
6613  * @param start the start of where to delete from.
6614  * @param end the end of the section to delete, if end == -1 it means the end of the string.
6615  * @returns @c EINA_TRUE if removed a PS, @c EINA_FALSE otherwise.
6616  */
6617 static Eina_Bool
6618 _evas_textblock_node_text_adjust_offsets_to_start(Evas_Object_Textblock *o,
6619       Evas_Object_Textblock_Node_Text *n, size_t start, int end)
6620 {
6621    Evas_Object_Textblock_Node_Format *last_node, *itr;
6622    Evas_Object_Textblock_Node_Text *new_node;
6623    int use_end = 1;
6624    int delta = 0;
6625    int first = 1;
6626    size_t pos = 0;
6627    int orig_end;
6628
6629    itr = n->format_node;
6630    if (!itr || (itr->text_node != n)) return EINA_FALSE;
6631
6632    orig_end = end;
6633    if ((end < 0) || ((size_t) end == eina_ustrbuf_length_get(n->unicode)))
6634      {
6635         use_end = 0;
6636      }
6637    else if (end > 0)
6638      {
6639         /* We don't want the last one */
6640         end--;
6641      }
6642
6643    /* If we are not removing the text node, all should stay in this text
6644     * node, otherwise, everything should move to the previous node */
6645    if ((start == 0) && !use_end)
6646      {
6647         new_node = _NODE_TEXT(EINA_INLIST_GET(n)->prev);
6648         if (!new_node)
6649           {
6650              new_node = n;
6651           }
6652      }
6653    else
6654      {
6655         new_node = n;
6656      }
6657
6658    /* Find the first node after start */
6659    while (itr && (itr->text_node == n))
6660      {
6661         pos += itr->offset;
6662         if (pos >= start)
6663           {
6664              break;
6665           }
6666         itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6667      }
6668
6669    if (!itr || (itr->text_node != n))
6670      {
6671         return EINA_FALSE;
6672      }
6673
6674    delta = orig_end - pos;
6675    itr->offset -= pos - start;
6676
6677    while (itr && (itr->text_node == n))
6678      {
6679         last_node = itr;
6680         itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6681
6682         if (!first)
6683           {
6684              pos += last_node->offset;
6685           }
6686
6687         /* start is negative when this gets relevant */
6688         if (use_end && (pos > (size_t) end))
6689           {
6690              last_node->offset -= delta;
6691              break;
6692           }
6693
6694         delta = orig_end - pos;
6695         if (!first)
6696           {
6697              last_node->offset = 0;
6698           }
6699         else
6700           {
6701              first = 0;
6702           }
6703         last_node->visible = EINA_FALSE;
6704
6705         if (!itr || (itr && (itr->text_node != n)))
6706           {
6707              /* Remove the PS, and return since it's the end of the node */
6708              if (_IS_PARAGRAPH_SEPARATOR(o, last_node->format))
6709                {
6710                   _evas_textblock_node_format_remove(o, last_node, 0);
6711                   return EINA_TRUE;
6712                }
6713
6714           }
6715         last_node->text_node = new_node;
6716      }
6717
6718    return EINA_FALSE;
6719 }
6720
6721 /**
6722  * @internal
6723  * Returns the first format in the range between start and end in the textblock
6724  * n.
6725  *
6726  * @param o the textblock object.
6727  * @param n the text node the positinos refer to.
6728  * @param start the start of where to delete from.
6729  * @param end the end of the section to delete, if end == -1 it means the end of the string.
6730  */
6731 static Evas_Object_Textblock_Node_Format *
6732 _evas_textblock_node_text_get_first_format_between(
6733       Evas_Object_Textblock_Node_Text *n, int start, int end)
6734 {
6735    Evas_Object_Textblock_Node_Format *itr;
6736    int use_end = 1;
6737    itr = n->format_node;
6738    if (end < 0) use_end = 0;
6739    while (itr && (itr->text_node == n))
6740      {
6741         start -= itr->offset;
6742         end -= itr->offset;
6743         if ((end <= 0) && use_end)
6744           {
6745              break;
6746           }
6747         if (start <= 0)
6748           {
6749              return itr;
6750           }
6751         itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6752      }
6753    return NULL;
6754 }
6755
6756 /**
6757  * Removes a text node and the corresponding format nodes.
6758  *
6759  * @param o the textblock objec.t
6760  * @param n the node to remove.
6761  */
6762 static void
6763 _evas_textblock_node_text_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *n)
6764 {
6765    _evas_textblock_node_text_adjust_offsets_to_start(o, n, 0, -1);
6766
6767    o->text_nodes = _NODE_TEXT(eina_inlist_remove(
6768            EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n)));
6769    _evas_textblock_node_text_free(n);
6770 }
6771
6772 /**
6773  * @internal
6774  * Return the position where the formats starts at.
6775  *
6776  * @param fmt the format to return the position of.
6777  * @return the position of the format in the text node it points to.
6778  */
6779 static size_t
6780 _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt)
6781 {
6782    Evas_Object_Textblock_Node_Text *text;
6783    Evas_Object_Textblock_Node_Format *base_format;
6784    Evas_Object_Textblock_Node_Format *itr;
6785    size_t position = 0;
6786
6787    if (!fmt) return 0;
6788    /* Find the main format node */
6789    text = fmt->text_node;
6790    base_format = text->format_node;
6791    EINA_INLIST_FOREACH(base_format, itr)
6792      {
6793         if (itr == fmt)
6794           {
6795              break;
6796           }
6797         position += itr->offset;
6798      }
6799    return position + fmt->offset;
6800 }
6801
6802 EAPI int
6803 evas_textblock_cursor_pos_get(const Evas_Textblock_Cursor *cur)
6804 {
6805    Evas_Object_Textblock *o;
6806    Evas_Object_Textblock_Node_Text *n;
6807    size_t npos = 0;
6808
6809    if (!cur) return -1;
6810    TB_NULL_CHECK(cur->node, 0);
6811    o = (Evas_Object_Textblock *)(cur->obj->object_data);
6812    n = o->text_nodes;
6813    while (n != cur->node)
6814      {
6815         npos += eina_ustrbuf_length_get(n->unicode);
6816         n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
6817      }
6818    return npos + cur->pos;
6819 }
6820
6821 EAPI void
6822 evas_textblock_cursor_pos_set(Evas_Textblock_Cursor *cur, int _pos)
6823 {
6824    Evas_Object_Textblock *o;
6825    Evas_Object_Textblock_Node_Text *n;
6826    size_t pos;
6827
6828    if (!cur) return;
6829    o = (Evas_Object_Textblock *)(cur->obj->object_data);
6830
6831    if (_pos < 0)
6832      {
6833         pos = 0;
6834      }
6835    else
6836      {
6837         pos = (size_t) _pos;
6838      }
6839
6840    n = o->text_nodes;
6841    while (n && (pos >= eina_ustrbuf_length_get(n->unicode)))
6842      {
6843         pos -= eina_ustrbuf_length_get(n->unicode);
6844         n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
6845      }
6846
6847    if (n)
6848      {
6849         cur->node = n;
6850         cur->pos = pos;
6851      }
6852    else if (o->text_nodes)
6853      {
6854         /* In case we went pass the last node, we need to put the cursor
6855          * at the absolute end. */
6856         Evas_Object_Textblock_Node_Text *last_n;
6857
6858         last_n = _NODE_TEXT(EINA_INLIST_GET(o->text_nodes)->last);
6859         pos = eina_ustrbuf_length_get(last_n->unicode);
6860
6861         cur->node = last_n;
6862         cur->pos = pos;
6863      }
6864
6865 }
6866
6867 EAPI Eina_Bool
6868 evas_textblock_cursor_line_set(Evas_Textblock_Cursor *cur, int line)
6869 {
6870    Evas_Object_Textblock *o;
6871    Evas_Object_Textblock_Line *ln;
6872    Evas_Object_Textblock_Item *it;
6873
6874    if (!cur) return EINA_FALSE;
6875    o = (Evas_Object_Textblock *)(cur->obj->object_data);
6876    if (!o->formatted.valid) _relayout(cur->obj);
6877
6878    ln = _find_layout_line_num(cur->obj, line);
6879    if (!ln) return EINA_FALSE;
6880    it = (Evas_Object_Textblock_Item *)ln->items;
6881    if (it)
6882      {
6883         cur->pos = it->text_pos;
6884         cur->node = it->text_node;
6885      }
6886    else
6887      {
6888         cur->pos = 0;
6889
6890         cur->node = o->text_nodes;
6891      }
6892    return EINA_TRUE;
6893 }
6894
6895 EAPI int
6896 evas_textblock_cursor_compare(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
6897 {
6898    Eina_Inlist *l1, *l2;
6899
6900    if (!cur1) return 0;
6901    if (!cur2) return 0;
6902    if (cur1->obj != cur2->obj) return 0;
6903    if ((!cur1->node) || (!cur2->node)) return 0;
6904    if (cur1->node == cur2->node)
6905      {
6906         if (cur1->pos < cur2->pos) return -1; /* cur1 < cur2 */
6907         else if (cur1->pos > cur2->pos) return 1; /* cur2 < cur1 */
6908         return 0;
6909      }
6910    for (l1 = EINA_INLIST_GET(cur1->node),
6911         l2 = EINA_INLIST_GET(cur1->node); (l1) || (l2);)
6912      {
6913         if (l1 == EINA_INLIST_GET(cur2->node)) return 1; /* cur2 < cur 1 */
6914         else if (l2 == EINA_INLIST_GET(cur2->node)) return -1; /* cur1 < cur 2 */
6915         else if (!l1) return -1; /* cur1 < cur 2 */
6916         else if (!l2) return 1; /* cur2 < cur 1 */
6917         l1 = l1->prev;
6918         l2 = l2->next;
6919      }
6920    return 0;
6921 }
6922
6923 EAPI void
6924 evas_textblock_cursor_copy(const Evas_Textblock_Cursor *cur, Evas_Textblock_Cursor *cur_dest)
6925 {
6926    if (!cur) return;
6927    if (!cur_dest) return;
6928    if (cur->obj != cur_dest->obj) return;
6929    cur_dest->pos = cur->pos;
6930    cur_dest->node = cur->node;
6931
6932 }
6933
6934
6935 /* text controls */
6936 /**
6937  * @internal
6938  * Free a text node. Shouldn't be used usually, it's better to use
6939  * @ref _evas_textblock_node_text_remove for most cases .
6940  *
6941  * @param n the text node to free
6942  * @see _evas_textblock_node_text_remove
6943  */
6944 static void
6945 _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n)
6946 {
6947    if (!n) return;
6948    eina_ustrbuf_free(n->unicode);
6949    if (n->utf8)
6950      free(n->utf8);
6951    if (n->par)
6952       n->par->text_node = NULL;
6953    free(n);
6954 }
6955
6956 /**
6957  * @internal
6958  * Create a new text node
6959  *
6960  * @return the new text node.
6961  */
6962 static Evas_Object_Textblock_Node_Text *
6963 _evas_textblock_node_text_new(void)
6964 {
6965    Evas_Object_Textblock_Node_Text *n;
6966
6967    n = calloc(1, sizeof(Evas_Object_Textblock_Node_Text));
6968    n->unicode = eina_ustrbuf_new();
6969    /* We want to layout each paragraph at least once. */
6970    n->dirty = EINA_TRUE;
6971    n->is_new = EINA_TRUE;
6972
6973    return n;
6974 }
6975
6976 /**
6977  * @internal
6978  * Break a paragraph. This does not add a PS but only splits the paragraph
6979  * where a ps was just added!
6980  *
6981  * @param cur the cursor to break at.
6982  * @param fnode the format node of the PS just added.
6983  * @return Returns no value.
6984  */
6985 static void
6986 _evas_textblock_cursor_break_paragraph(Evas_Textblock_Cursor *cur,
6987                               Evas_Object_Textblock_Node_Format *fnode)
6988 {
6989    Evas_Object_Textblock *o;
6990    Evas_Object_Textblock_Node_Text *n;
6991
6992    if (!cur) return;
6993    o = (Evas_Object_Textblock *)(cur->obj->object_data);
6994
6995    n = _evas_textblock_node_text_new();
6996    o->text_nodes = _NODE_TEXT(eina_inlist_append_relative(
6997                    EINA_INLIST_GET(o->text_nodes),
6998                    EINA_INLIST_GET(n),
6999                    EINA_INLIST_GET(cur->node)));
7000    /* Handle text and format changes. */
7001    if (cur->node)
7002      {
7003         Evas_Object_Textblock_Node_Format *nnode;
7004         size_t len, start;
7005         const Eina_Unicode *text;
7006
7007         /* If there was a format node in the delete range,
7008          * make it our format and update the text_node fields,
7009          * otherwise, use the paragraph separator
7010          * of the previous paragraph. */
7011         nnode  = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7012         if (nnode && (nnode->text_node == cur->node))
7013           {
7014              n->format_node = nnode;
7015              nnode->offset--; /* We don't have to take the replacement char
7016                                  into account anymore */
7017              while (nnode && (nnode->text_node == cur->node))
7018                {
7019                   nnode->text_node = n;
7020                   nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
7021                }
7022           }
7023         else
7024           {
7025              n->format_node = fnode;
7026           }
7027
7028         /* cur->pos now points to the PS, move after. */
7029         start = cur->pos + 1;
7030         len = eina_ustrbuf_length_get(cur->node->unicode) - start;
7031         if (len > 0)
7032           {
7033              text = eina_ustrbuf_string_get(cur->node->unicode);
7034              eina_ustrbuf_append_length(n->unicode, text + start, len);
7035              eina_ustrbuf_remove(cur->node->unicode, start, start + len);
7036              cur->node->dirty = EINA_TRUE;
7037           }
7038      }
7039    else
7040      {
7041         fnode = o->format_nodes;
7042         if (fnode)
7043           {
7044              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->last);
7045           }
7046         n->format_node = fnode;
7047      }
7048 }
7049
7050 /**
7051  * @internal
7052  * Set the node and offset of all the curs after cur.
7053  *
7054  * @param cur the cursor.
7055  * @param n the current textblock node.
7056  * @param new_node the new node to set.
7057  */
7058 static void
7059 _evas_textblock_cursors_set_node(Evas_Object_Textblock *o,
7060       const Evas_Object_Textblock_Node_Text *n,
7061       Evas_Object_Textblock_Node_Text *new_node)
7062 {
7063    Eina_List *l;
7064    Evas_Textblock_Cursor *data;
7065
7066    if (n == o->cursor->node)
7067      {
7068         o->cursor->pos = 0;
7069         o->cursor->node = new_node;
7070      }
7071    EINA_LIST_FOREACH(o->cursors, l, data)
7072      {
7073         if (n == data->node)
7074           {
7075              data->pos = 0;
7076              data->node = new_node;
7077           }
7078      }
7079 }
7080
7081 /**
7082  * @internal
7083  * Update the offset of all the cursors after cur.
7084  *
7085  * @param cur the cursor.
7086  * @param n the current textblock node.
7087  * @param start the starting pos.
7088  * @param offset how much to adjust (can be negative).
7089  */
7090 static void
7091 _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor *cur,
7092       const Evas_Object_Textblock_Node_Text *n,
7093       size_t start, int offset)
7094 {
7095    Eina_List *l;
7096    Evas_Textblock_Cursor *data;
7097    Evas_Object_Textblock *o;
7098    o = (Evas_Object_Textblock *)(cur->obj->object_data);
7099
7100    if (cur != o->cursor)
7101      {
7102         if ((n == o->cursor->node) &&
7103               (o->cursor->pos > start))
7104           {
7105              if ((offset < 0) && (o->cursor->pos <= (size_t) (-1 * offset)))
7106                {
7107                   o->cursor->pos = 0;
7108                }
7109              else
7110                {
7111                   o->cursor->pos += offset;
7112                }
7113           }
7114      }
7115    EINA_LIST_FOREACH(o->cursors, l, data)
7116      {
7117         if (data != cur)
7118           {
7119              if ((n == data->node) &&
7120                    (data->pos > start))
7121                {
7122                   if ((offset < 0) && (data->pos <= (size_t) (-1 * offset)))
7123                     {
7124                        data->pos = 0;
7125                     }
7126                   else
7127                     {
7128                        data->pos += offset;
7129                     }
7130                }
7131              else if (!data->node)
7132                {
7133                   data->node = o->text_nodes;
7134                   data->pos = 0;
7135                }
7136           }
7137      }
7138 }
7139
7140 /**
7141  * @internal
7142  * Mark that the textblock has changed.
7143  *
7144  * @param o the textblock object.
7145  * @param obj the evas object.
7146  */
7147 static void
7148 _evas_textblock_changed(Evas_Object_Textblock *o, Evas_Object *obj)
7149 {
7150    o->formatted.valid = 0;
7151    o->native.valid = 0;
7152    o->content_changed = 1;
7153    if (o->markup_text)
7154      {
7155         free(o->markup_text);
7156         o->markup_text = NULL;
7157      }
7158
7159    evas_object_change(obj);
7160 }
7161
7162 static void
7163 _evas_textblock_invalidate_all(Evas_Object_Textblock *o)
7164 {
7165    Evas_Object_Textblock_Node_Text *n;
7166
7167    EINA_INLIST_FOREACH(o->text_nodes, n)
7168      {
7169         n->dirty = EINA_TRUE;
7170      }
7171 }
7172
7173 EAPI int
7174 evas_textblock_cursor_text_append(Evas_Textblock_Cursor *cur, const char *_text)
7175 {
7176    Evas_Object_Textblock *o;
7177    Evas_Object_Textblock_Node_Text *n;
7178    Evas_Object_Textblock_Node_Format *fnode = NULL;
7179    Eina_Unicode *text;
7180    int len = 0;
7181
7182    if (!cur) return 0;
7183    text = eina_unicode_utf8_to_unicode(_text, &len);
7184    o = (Evas_Object_Textblock *)(cur->obj->object_data);
7185
7186    n = cur->node;
7187    if (n)
7188      {
7189         Evas_Object_Textblock_Node_Format *nnode;
7190         fnode = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
7191         fnode = _evas_textblock_node_format_last_at_off(fnode);
7192         /* find the node after the current in the same paragraph
7193          * either we find one and then take the next, or we try to get
7194          * the first for the paragraph which must be after our position  */
7195         if (fnode)
7196           {
7197              if (!evas_textblock_cursor_format_is_visible_get(cur))
7198                {
7199                   nnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7200                   if (nnode && (nnode->text_node == n))
7201                     {
7202                        fnode = nnode;
7203                     }
7204                   else
7205                     {
7206                        fnode = NULL;
7207                     }
7208                }
7209           }
7210         else
7211           {
7212              fnode = n->format_node;
7213           }
7214      }
7215    else if (o->text_nodes)
7216      {
7217         n = cur->node = o->text_nodes;
7218         cur->pos = 0;
7219      }
7220    else
7221      {
7222         n = _evas_textblock_node_text_new();
7223         o->text_nodes = _NODE_TEXT(eina_inlist_append(
7224                    EINA_INLIST_GET(o->text_nodes),
7225                    EINA_INLIST_GET(n)));
7226         cur->node = n;
7227      }
7228
7229    eina_ustrbuf_insert_length(n->unicode, text, len, cur->pos);
7230    /* Advance the formats */
7231    if (fnode && (fnode->text_node == cur->node))
7232      fnode->offset += len;
7233
7234    /* Update all the cursors after our position. */
7235    _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, len);
7236
7237    _evas_textblock_changed(o, cur->obj);
7238    n->dirty = EINA_TRUE;
7239    free(text);
7240
7241    if (!o->cursor->node)
7242       o->cursor->node = o->text_nodes;
7243    return len;
7244 }
7245
7246 EAPI int
7247 evas_textblock_cursor_text_prepend(Evas_Textblock_Cursor *cur, const char *_text)
7248 {
7249    int len;
7250    /*append is essentially prepend without advancing */
7251    len = evas_textblock_cursor_text_append(cur, _text);
7252    if (len == 0) return 0;
7253    cur->pos += len; /*Advance */
7254    return len;
7255 }
7256
7257 /**
7258  * @internal
7259  * Free a format node
7260  *
7261  * @param o the textblock object
7262  * @param n the format node to free
7263  */
7264 static void
7265 _evas_textblock_node_format_free(Evas_Object_Textblock *o,
7266       Evas_Object_Textblock_Node_Format *n)
7267 {
7268    if (!n) return;
7269    eina_stringshare_del(n->format);
7270    eina_stringshare_del(n->orig_format);
7271    if (n->anchor == ANCHOR_ITEM)
7272       o->anchors_item = eina_list_remove(o->anchors_item, n);
7273    else if (n->anchor == ANCHOR_A)
7274       o->anchors_a = eina_list_remove(o->anchors_a, n);
7275    free(n);
7276 }
7277
7278 /**
7279  * @internal
7280  * Create a new format node.
7281  *
7282  * @param format the text to create the format node from.
7283  * @param o the textblock object.
7284  * @return Returns the new format node
7285  */
7286 static Evas_Object_Textblock_Node_Format *
7287 _evas_textblock_node_format_new(Evas_Object_Textblock *o, const char *_format)
7288 {
7289    Evas_Object_Textblock_Node_Format *n;
7290    const char *format = _format;
7291    const char *pre_stripped_format = NULL;
7292
7293    n = calloc(1, sizeof(Evas_Object_Textblock_Node_Format));
7294    /* Create orig_format and format */
7295    if (format[0] == '<')
7296      {
7297         const char *match;
7298         size_t format_len;
7299         size_t replace_len;
7300
7301         format++; /* Advance after '<' */
7302         format_len = strlen(format);
7303         if ((format_len > 0) && format[format_len - 1] == '>')
7304           {
7305              format_len--; /* We don't care about '>' */
7306              /* Check if it closes itself. Skip the </> case. */
7307              if ((format_len > 1) && format[format_len - 1] == '/')
7308                {
7309                   format_len--; /* We don't care about '/' */
7310                   n->own_closer = EINA_TRUE;
7311                }
7312           }
7313
7314         if (!o->style_user || !(match = _style_match_tag(o->style_user, format,
7315                     format_len, &replace_len)))
7316           {
7317              match = _style_match_tag(o->style, format, format_len,
7318                    &replace_len);
7319           }
7320
7321         if (match)
7322           {
7323              if (match[0] != '-')
7324                {
7325                   n->opener = EINA_TRUE;
7326                   if (match[0] != '+')
7327                     {
7328                        n->own_closer = EINA_TRUE;
7329                     }
7330                }
7331
7332              pre_stripped_format = match;
7333           }
7334         else
7335           {
7336              if (format[0] == '/')
7337                {
7338                   format++;
7339                   format_len--;
7340                }
7341              else
7342                {
7343                   n->opener = EINA_TRUE;
7344                }
7345           }
7346
7347         n->orig_format = eina_stringshare_add_length(format, format_len);
7348
7349         if (!pre_stripped_format)
7350            pre_stripped_format = n->orig_format;
7351      }
7352    /* Just use as is, it's a special format. */
7353    else
7354      {
7355         const char *tmp = format;
7356         if (format[0] != '-')
7357           {
7358              n->opener = EINA_TRUE;
7359              if (format[0] != '+')
7360                {
7361                   n->own_closer = EINA_TRUE;
7362                }
7363           }
7364         if ((*tmp == '+') || (*tmp == '-'))
7365           {
7366              tmp++;
7367              while (*tmp == ' ') tmp++;
7368           }
7369         n->orig_format = eina_stringshare_add(tmp);
7370         pre_stripped_format = n->orig_format;
7371      }
7372
7373    /* Strip format */
7374      {
7375         const char *tmp = pre_stripped_format;
7376         if ((*tmp == '+') || (*tmp == '-'))
7377           {
7378              tmp++;
7379              while (*tmp == ' ') tmp++;
7380           }
7381         n->format = eina_stringshare_add(tmp);
7382      }
7383    format = n->format;
7384
7385    _evas_textblock_format_is_visible(n, format);
7386    if (n->anchor == ANCHOR_A)
7387      {
7388         o->anchors_a = eina_list_append(o->anchors_a, n);
7389      }
7390    else if (n->anchor == ANCHOR_ITEM)
7391      {
7392         o->anchors_item = eina_list_append(o->anchors_item, n);
7393      }
7394    n->is_new = EINA_TRUE;
7395
7396    return n;
7397 }
7398
7399 static Eina_Bool
7400 _evas_textblock_cursor_is_at_the_end(const Evas_Textblock_Cursor *cur)
7401 {
7402    const Eina_Unicode *text;
7403
7404    if (!cur) return EINA_FALSE;
7405    TB_NULL_CHECK(cur->node, EINA_FALSE);
7406    text = eina_ustrbuf_string_get(cur->node->unicode);
7407    if ((cur->pos - 1) > eina_ustrbuf_length_get(cur->node->unicode)) return EINA_FALSE;
7408    return ((text[cur->pos] == 0) && (!EINA_INLIST_GET(cur->node)->next)) ?
7409               EINA_TRUE : EINA_FALSE;
7410 }
7411
7412 EAPI Eina_Bool
7413 evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, const char *format)
7414 {
7415    Evas_Object_Textblock *o;
7416    Evas_Object_Textblock_Node_Format *n;
7417    Eina_Bool is_visible;
7418
7419    if (!cur) return EINA_FALSE;
7420    if ((!format) || (format[0] == 0)) return EINA_FALSE;
7421    o = (Evas_Object_Textblock *)(cur->obj->object_data);
7422    /* We should always have at least one text node */
7423    if (!o->text_nodes)
7424      {
7425         evas_textblock_cursor_text_prepend(cur, "");
7426      }
7427
7428    n = _evas_textblock_node_format_new(o, format);
7429    is_visible = n->visible;
7430    format = n->format;
7431    if (!cur->node)
7432      {
7433         o->format_nodes = _NODE_FORMAT(eina_inlist_append(
7434                  EINA_INLIST_GET(o->format_nodes),
7435                  EINA_INLIST_GET(n)));
7436         cur->pos = 0;
7437         n->text_node = (EINA_INLIST_GET(n)->prev) ?
7438            _NODE_FORMAT(EINA_INLIST_GET(n)->prev)->text_node :
7439            o->text_nodes;
7440         cur->node = n->text_node;
7441      }
7442    else
7443      {
7444         Evas_Object_Textblock_Node_Format *fmt;
7445         fmt = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
7446         n->text_node = cur->node;
7447         if (!fmt)
7448           {
7449              o->format_nodes = _NODE_FORMAT(eina_inlist_prepend(
7450                       EINA_INLIST_GET(o->format_nodes),
7451                       EINA_INLIST_GET(n)));
7452              n->offset = cur->pos;
7453           }
7454         else
7455           {
7456              if (evas_textblock_cursor_format_is_visible_get(cur))
7457                {
7458                   o->format_nodes = _NODE_FORMAT(eina_inlist_prepend_relative(
7459                            EINA_INLIST_GET(o->format_nodes),
7460                            EINA_INLIST_GET(n),
7461                            EINA_INLIST_GET(fmt)
7462                            ));
7463                   n->offset = fmt->offset;
7464                   if (fmt->text_node->format_node == fmt)
7465                     {
7466                        fmt->text_node->format_node = n;
7467                     }
7468                }
7469              else
7470                {
7471                   fmt = _evas_textblock_node_format_last_at_off(fmt);
7472                   o->format_nodes = _NODE_FORMAT(eina_inlist_append_relative(
7473                            EINA_INLIST_GET(o->format_nodes),
7474                            EINA_INLIST_GET(n),
7475                            EINA_INLIST_GET(fmt)
7476                            ));
7477                   if (fmt->text_node != cur->node)
7478                     {
7479                        n->offset = cur->pos;
7480                     }
7481                   else
7482                     {
7483                        n->offset = cur->pos -
7484                           _evas_textblock_node_format_pos_get(fmt);
7485                     }
7486                }
7487           }
7488         /* Adjust differently if we insert a format char */
7489         if (is_visible)
7490           {
7491              _evas_textblock_node_format_adjust_offset(o, cur->node, n,
7492                    -(n->offset - 1));
7493           }
7494         else
7495           {
7496              _evas_textblock_node_format_adjust_offset(o, cur->node, n,
7497                    -n->offset);
7498           }
7499
7500         if (!fmt || (fmt->text_node != cur->node))
7501           {
7502              cur->node->format_node = n;
7503           }
7504      }
7505    if (is_visible && cur->node)
7506      {
7507         Eina_Unicode insert_char;
7508         /* Insert a visual representation according to the type of the
7509            format */
7510         if (_IS_PARAGRAPH_SEPARATOR(o, format))
7511            insert_char = _PARAGRAPH_SEPARATOR;
7512         else if (_IS_LINE_SEPARATOR(format))
7513            insert_char = _NEWLINE;
7514         else if (_IS_TAB(format))
7515            insert_char = _TAB;
7516         else
7517            insert_char = _REPLACEMENT_CHAR;
7518
7519         eina_ustrbuf_insert_char(cur->node->unicode, insert_char, cur->pos);
7520
7521         /* Advance all the cursors after our cursor */
7522         _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, 1);
7523         if (_IS_PARAGRAPH_SEPARATOR(o, format))
7524           {
7525              _evas_textblock_cursor_break_paragraph(cur, n);
7526           }
7527         else
7528           {
7529              /* Handle visible format nodes here */
7530              cur->node->dirty = EINA_TRUE;
7531              n->is_new = EINA_FALSE;
7532           }
7533      }
7534    else
7535      {
7536         o->format_changed = EINA_TRUE;
7537      }
7538
7539    _evas_textblock_changed(o, cur->obj);
7540
7541    if (!o->cursor->node)
7542       o->cursor->node = o->text_nodes;
7543    return is_visible;
7544 }
7545
7546 EAPI Eina_Bool
7547 evas_textblock_cursor_format_prepend(Evas_Textblock_Cursor *cur, const char *format)
7548 {
7549    Eina_Bool is_visible;
7550    /* append is essentially prepend without advancing */
7551    is_visible = evas_textblock_cursor_format_append(cur, format);
7552    if (is_visible)
7553      {
7554         /* Advance after the replacement char */
7555         evas_textblock_cursor_char_next(cur);
7556      }
7557
7558    return is_visible;
7559 }
7560
7561
7562 EAPI void
7563 evas_textblock_cursor_char_delete(Evas_Textblock_Cursor *cur)
7564 {
7565    Evas_Object_Textblock *o;
7566    Evas_Object_Textblock_Node_Text *n, *n2;
7567    const Eina_Unicode *text;
7568    int chr, ind, ppos;
7569
7570    if (!cur || !cur->node) return;
7571    o = (Evas_Object_Textblock *)(cur->obj->object_data);
7572    n = cur->node;
7573
7574    text = eina_ustrbuf_string_get(n->unicode);
7575    ind = cur->pos;
7576    if (text[ind])
7577       chr = text[ind++];
7578    else
7579       chr = 0;
7580
7581    if (chr == 0) return;
7582    ppos = cur->pos;
7583    eina_ustrbuf_remove(n->unicode, cur->pos, ind);
7584    /* Remove a format node if needed, and remove the char only if the
7585     * fmt node is not visible */
7586      {
7587         Eina_Bool should_merge = EINA_FALSE;
7588         Evas_Object_Textblock_Node_Format *fmt, *fmt2;
7589         fmt = _evas_textblock_cursor_node_format_at_pos_get(cur);
7590         if (fmt)
7591           {
7592              const char *format = NULL;
7593              Evas_Object_Textblock_Node_Format *last_fmt;
7594              /* If there's a PS it must be the last become it delimits paragraphs */
7595              last_fmt = _evas_textblock_node_format_last_at_off(fmt);
7596              format = last_fmt->format;
7597              if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
7598                {
7599                   /* If it was a paragraph separator, we should merge the
7600                    * current with the next, there must be a next. */
7601                   should_merge = EINA_TRUE;
7602                }
7603              /* If a singnular, mark as invisible, so we'll delete it. */
7604              if (!format || last_fmt->own_closer)
7605                {
7606                   last_fmt->visible = EINA_FALSE;
7607                }
7608           }
7609
7610         fmt2 = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
7611         fmt2 = _evas_textblock_node_format_last_at_off(fmt2);
7612         _evas_textblock_node_format_adjust_offset(o, cur->node, fmt2,
7613               -(ind - cur->pos));
7614
7615         if (should_merge)
7616           {
7617              _evas_textblock_cursor_nodes_merge(cur);
7618           }
7619
7620         _evas_textblock_node_format_remove_matching(o, fmt);
7621      }
7622
7623    if (cur->pos == eina_ustrbuf_length_get(n->unicode))
7624      {
7625         n2 = _NODE_TEXT(EINA_INLIST_GET(n)->next);
7626         if (n2)
7627           {
7628              cur->node = n2;
7629              cur->pos = 0;
7630           }
7631      }
7632
7633    _evas_textblock_cursors_update_offset(cur, n, ppos, -(ind - ppos));
7634    _evas_textblock_changed(o, cur->obj);
7635    cur->node->dirty = EINA_TRUE;
7636 }
7637
7638 EAPI void
7639 evas_textblock_cursor_range_delete(Evas_Textblock_Cursor *cur1, Evas_Textblock_Cursor *cur2)
7640 {
7641    Evas_Object_Textblock_Node_Format *fnode = NULL;
7642    Evas_Object_Textblock *o;
7643    Evas_Object_Textblock_Node_Text *n1, *n2;
7644    Eina_Bool should_merge = EINA_FALSE, reset_cursor = EINA_FALSE;
7645
7646    if (!cur1 || !cur1->node) return;
7647    if (!cur2 || !cur2->node) return;
7648    if (cur1->obj != cur2->obj) return;
7649    o = (Evas_Object_Textblock *)(cur1->obj->object_data);
7650    if (evas_textblock_cursor_compare(cur1, cur2) > 0)
7651      {
7652         Evas_Textblock_Cursor *tc;
7653
7654         tc = cur1;
7655         cur1 = cur2;
7656         cur2 = tc;
7657      }
7658    n1 = cur1->node;
7659    n2 = cur2->node;
7660    if ((evas_textblock_cursor_compare(o->cursor, cur1) >= 0) &&
7661          (evas_textblock_cursor_compare(cur2, o->cursor) >= 0))
7662      {
7663         reset_cursor = EINA_TRUE;
7664      }
7665
7666
7667    if (n1 == n2)
7668      {
7669         if ((cur1->pos == 0) &&
7670               (cur2->pos == eina_ustrbuf_length_get(n1->unicode)))
7671           {
7672              /* Remove the whole node. */
7673              Evas_Object_Textblock_Node_Text *n =
7674                 _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7675              if (n)
7676                {
7677                   should_merge = EINA_TRUE;
7678                }
7679              else
7680                {
7681                   n = _NODE_TEXT(EINA_INLIST_GET(n1)->prev);
7682                   /* Short path */
7683                   if (!n)
7684                     {
7685                        /* Clear the whole textblock - do it nicer. */
7686                        evas_object_textblock_text_markup_set(cur1->obj, "");
7687                        return;
7688                     }
7689                }
7690           }
7691         else
7692           {
7693              should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o,
7694                    n1, cur1->pos, cur2->pos);
7695           }
7696         eina_ustrbuf_remove(n1->unicode, cur1->pos, cur2->pos);
7697         _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos, - (cur2->pos - cur1->pos));
7698      }
7699    else
7700      {
7701         Evas_Object_Textblock_Node_Text *n;
7702         int len;
7703         _evas_textblock_node_text_adjust_offsets_to_start(o, n1, cur1->pos, -1);
7704         n = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7705         /* Remove all the text nodes between */
7706         while (n && (n != n2))
7707           {
7708              Evas_Object_Textblock_Node_Text *nnode;
7709
7710              nnode = _NODE_TEXT(EINA_INLIST_GET(n)->next);
7711              _evas_textblock_node_text_adjust_offsets_to_start(o, n, 0, -1);
7712              _evas_textblock_nodes_merge(o, n1);
7713              n = nnode;
7714           }
7715         should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o, n2,
7716               0, cur2->pos);
7717
7718         /* Remove the formats and the strings in the first and last nodes */
7719         len = eina_ustrbuf_length_get(n1->unicode);
7720         eina_ustrbuf_remove(n1->unicode, cur1->pos, len);
7721         eina_ustrbuf_remove(n2->unicode, 0, cur2->pos);
7722         /* Merge the nodes because we removed the PS */
7723         _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos,
7724                                               -cur1->pos);
7725         _evas_textblock_cursors_update_offset(cur2, cur2->node, 0, -cur2->pos);
7726         cur2->pos = 0;
7727         _evas_textblock_nodes_merge(o, n1);
7728      }
7729    fnode = _evas_textblock_cursor_node_format_at_pos_get(cur1);
7730
7731    n1 = cur1->node;
7732    n2 = cur2->node;
7733    n1->dirty = n2->dirty = EINA_TRUE;
7734
7735    if (should_merge)
7736      {
7737         /* We call this function instead of the cursor one because we already
7738          * updated the cursors */
7739         _evas_textblock_nodes_merge(o, n1);
7740      }
7741    _evas_textblock_node_format_remove_matching(o, fnode);
7742
7743    evas_textblock_cursor_copy(cur1, cur2);
7744    if (reset_cursor)
7745      evas_textblock_cursor_copy(cur1, o->cursor);
7746
7747    _evas_textblock_changed(o, cur1->obj);
7748 }
7749
7750
7751 EAPI char *
7752 evas_textblock_cursor_content_get(const Evas_Textblock_Cursor *cur)
7753 {
7754    if (!cur || !cur->node) return NULL;
7755    if (evas_textblock_cursor_format_is_visible_get(cur))
7756      {
7757         Eina_Strbuf *buf;
7758         Evas_Object_Textblock_Node_Format *fnode;
7759         char *ret;
7760         fnode = _evas_textblock_node_visible_at_pos_get(
7761                  evas_textblock_cursor_format_get(cur));
7762
7763         buf = eina_strbuf_new();
7764         _markup_get_format_append(buf, fnode);
7765         ret = eina_strbuf_string_steal(buf);
7766         eina_strbuf_free(buf);
7767
7768         return ret;
7769      }
7770    else
7771      {
7772         const Eina_Unicode *ustr;
7773         Eina_Unicode buf[2];
7774         char *s;
7775
7776         ustr = eina_ustrbuf_string_get(cur->node->unicode);
7777         buf[0] = ustr[cur->pos];
7778         buf[1] = 0;
7779         s = eina_unicode_unicode_to_utf8(buf, NULL);
7780
7781         return s;
7782      }
7783 }
7784
7785 static char *
7786 _evas_textblock_cursor_range_text_markup_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2)
7787 {
7788    Evas_Object_Textblock_Node_Text *tnode;
7789    Eina_Strbuf *buf;
7790    Evas_Textblock_Cursor *cur2;
7791    buf = eina_strbuf_new();
7792
7793    if (!cur1 || !cur1->node) return NULL;
7794    if (!_cur2 || !_cur2->node) return NULL;
7795    if (cur1->obj != _cur2->obj) return NULL;
7796    if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
7797      {
7798         const Evas_Textblock_Cursor *tc;
7799
7800         tc = cur1;
7801         cur1 = _cur2;
7802         _cur2 = tc;
7803      }
7804    /* Work on a local copy of the cur */
7805    cur2 = alloca(sizeof(Evas_Textblock_Cursor));
7806    cur2->obj = _cur2->obj;
7807    evas_textblock_cursor_copy(_cur2, cur2);
7808
7809    /* Parse the text between the cursors. */
7810    for (tnode = cur1->node ; tnode ;
7811          tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next))
7812      {
7813         Evas_Object_Textblock_Node_Format *fnode;
7814         Eina_Unicode *text_base, *text;
7815         int off = 0;
7816
7817         text_base = text =
7818            eina_unicode_strndup(eina_ustrbuf_string_get(tnode->unicode),
7819                                 eina_ustrbuf_length_get(tnode->unicode));
7820         if (tnode == cur2->node)
7821           {
7822              fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7823                    cur1->pos, cur2->pos);
7824           }
7825         else if (tnode == cur1->node)
7826           {
7827              fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7828                    cur1->pos, -1);
7829           }
7830         else
7831           {
7832              fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7833                    0, -1);
7834           }
7835         /* Init the offset so the first one will count starting from cur1->pos
7836          * and not the previous format node */
7837         if (tnode == cur1->node)
7838           {
7839              if (fnode)
7840                {
7841                   off = _evas_textblock_node_format_pos_get(fnode) -
7842                      cur1->pos - fnode->offset;
7843                }
7844              text += cur1->pos;
7845           }
7846         else
7847           {
7848              off = 0;
7849           }
7850         while (fnode && (fnode->text_node == tnode))
7851           {
7852              Eina_Unicode tmp_ch;
7853              off += fnode->offset;
7854              if ((tnode == cur2->node) &&
7855                    ((size_t) (text - text_base + off) >= cur2->pos))
7856                {
7857                   break;
7858                }
7859              /* No need to skip on the first run */
7860              tmp_ch = text[off];
7861              text[off] = 0; /* Null terminate the part of the string */
7862              _markup_get_text_append(buf, text);
7863              _markup_get_format_append(buf, fnode);
7864              text[off] = tmp_ch; /* Restore the char */
7865              text += off;
7866              if (fnode->visible)
7867                {
7868                   off = -1;
7869                   text++;
7870                }
7871              else
7872                {
7873                   off = 0;
7874                }
7875              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7876           }
7877         /* If we got to the last node, stop and add the rest outside */
7878         if (cur2->node == tnode)
7879           {
7880              /* Add the rest, skip replacement */
7881              /* Don't go past the second cursor pos */
7882              text_base[cur2->pos] = '\0';
7883              _markup_get_text_append(buf, text);
7884              free(text_base);
7885              break;
7886           }
7887         else
7888           {
7889              /* Add the rest, skip replacement */
7890              _markup_get_text_append(buf, text);
7891              free(text_base);
7892           }
7893      }
7894    /* return the string */
7895      {
7896         char *ret;
7897         ret = eina_strbuf_string_steal(buf);
7898         eina_strbuf_free(buf);
7899         return ret;
7900      }
7901 }
7902
7903 static char *
7904 _evas_textblock_cursor_range_text_plain_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2)
7905 {
7906    Eina_UStrbuf *buf;
7907    Evas_Object_Textblock_Node_Text *n1, *n2;
7908    Evas_Textblock_Cursor *cur2;
7909
7910    buf = eina_ustrbuf_new();
7911
7912    if (!cur1 || !cur1->node) return NULL;
7913    if (!_cur2 || !_cur2->node) return NULL;
7914    if (cur1->obj != _cur2->obj) return NULL;
7915    if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
7916      {
7917         const Evas_Textblock_Cursor *tc;
7918
7919         tc = cur1;
7920         cur1 = _cur2;
7921         _cur2 = tc;
7922      }
7923    n1 = cur1->node;
7924    n2 = _cur2->node;
7925    /* Work on a local copy of the cur */
7926    cur2 = alloca(sizeof(Evas_Textblock_Cursor));
7927    cur2->obj = _cur2->obj;
7928    evas_textblock_cursor_copy(_cur2, cur2);
7929
7930
7931    if (n1 == n2)
7932      {
7933         const Eina_Unicode *tmp;
7934         tmp = eina_ustrbuf_string_get(n1->unicode);
7935         eina_ustrbuf_append_length(buf, tmp + cur1->pos, cur2->pos - cur1->pos);
7936      }
7937    else
7938      {
7939         const Eina_Unicode *tmp;
7940         tmp = eina_ustrbuf_string_get(n1->unicode);
7941         eina_ustrbuf_append(buf, tmp + cur1->pos);
7942         n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7943         while (n1 != n2)
7944           {
7945              tmp = eina_ustrbuf_string_get(n1->unicode);
7946              eina_ustrbuf_append_length(buf, tmp,
7947                    eina_ustrbuf_length_get(n1->unicode));
7948              n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7949           }
7950         tmp = eina_ustrbuf_string_get(n2->unicode);
7951         eina_ustrbuf_append_length(buf, tmp, cur2->pos);
7952      }
7953
7954    /* Free and return */
7955      {
7956         char *ret;
7957         ret = eina_unicode_unicode_to_utf8(eina_ustrbuf_string_get(buf), NULL);
7958         eina_ustrbuf_free(buf);
7959         return ret;
7960      }
7961 }
7962
7963 EAPI Eina_List *
7964 evas_textblock_cursor_range_formats_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
7965 {
7966    Evas_Object *obj;
7967    Eina_List *ret = NULL;
7968    Evas_Object_Textblock_Node_Text *n1, *n2;
7969    Evas_Object_Textblock_Node_Format *first, *last;
7970    if (!cur1 || !cur1->node) return NULL;
7971    if (!cur2 || !cur2->node) return NULL;
7972    if (cur1->obj != cur2->obj) return NULL;
7973
7974    obj = cur1->obj;
7975    TB_HEAD_RETURN(NULL);
7976
7977    if (evas_textblock_cursor_compare(cur1, cur2) > 0)
7978      {
7979         const Evas_Textblock_Cursor *tc;
7980
7981         tc = cur1;
7982         cur1 = cur2;
7983         cur2 = tc;
7984      }
7985    n1 = cur1->node;
7986    n2 = cur2->node;
7987
7988    /* FIXME: Change first and last getting to format_before_or_at_pos_get */
7989
7990    last = n2->format_node;
7991
7992    /* If n2->format_node is NULL, we don't have formats in the tb/range. */
7993    if (!last)
7994       return NULL;
7995    /* If the found format is on our text node, we should go to the last
7996     * one, otherwise, the one we found is good enough. */
7997    if (last->text_node == n2)
7998      {
7999         Evas_Object_Textblock_Node_Format *fnode = last;
8000         while (fnode && (fnode->text_node == n2))
8001           {
8002              last = fnode;
8003              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
8004           }
8005      }
8006
8007    /* If the first format node is within the range (i.e points to n1) or if
8008     * we have other formats in the range, go through them */
8009    first = n1->format_node;
8010    if ((first->text_node == n1) || (first != last))
8011      {
8012         Evas_Object_Textblock_Node_Format *fnode = first;
8013         /* Go to the first one in the range */
8014         if (first->text_node != n1)
8015           {
8016              first = _NODE_FORMAT(EINA_INLIST_GET(first)->next);
8017           }
8018
8019         while (fnode)
8020           {
8021              ret = eina_list_append(ret, fnode);
8022              if (fnode == last)
8023                 break;
8024              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
8025           }
8026      }
8027
8028    return ret;
8029
8030 }
8031
8032 EAPI char *
8033 evas_textblock_cursor_range_text_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2, Evas_Textblock_Text_Type format)
8034 {
8035    if (format == EVAS_TEXTBLOCK_TEXT_MARKUP)
8036       return _evas_textblock_cursor_range_text_markup_get(cur1, cur2);
8037    else if (format == EVAS_TEXTBLOCK_TEXT_PLAIN)
8038       return _evas_textblock_cursor_range_text_plain_get(cur1, cur2);
8039    else
8040       return NULL; /* Not yet supported */
8041 }
8042
8043 EAPI const char *
8044 evas_textblock_cursor_paragraph_text_get(const Evas_Textblock_Cursor *cur)
8045 {
8046    Evas_Textblock_Cursor cur1, cur2;
8047    if (!cur) return NULL;
8048    TB_NULL_CHECK(cur->node, NULL);
8049    if (cur->node->utf8)
8050      {
8051         free(cur->node->utf8);
8052      }
8053    cur1.obj = cur2.obj = cur->obj;
8054    cur1.node = cur2.node = cur->node;
8055    evas_textblock_cursor_paragraph_char_first(&cur1);
8056    evas_textblock_cursor_paragraph_char_last(&cur2);
8057
8058    cur->node->utf8 = evas_textblock_cursor_range_text_get(&cur1, &cur2,
8059          EVAS_TEXTBLOCK_TEXT_MARKUP);
8060    return cur->node->utf8;
8061 }
8062
8063 EAPI int
8064 evas_textblock_cursor_paragraph_text_length_get(const Evas_Textblock_Cursor *cur)
8065 {
8066    int len;
8067    if (!cur) return -1;
8068    TB_NULL_CHECK(cur->node, -1);
8069    len = eina_ustrbuf_length_get(cur->node->unicode);
8070
8071    if (EINA_INLIST_GET(cur->node)->next)
8072       return len - 1; /* Remove the paragraph separator */
8073    else
8074       return len;
8075 }
8076
8077 EAPI const Evas_Object_Textblock_Node_Format *
8078 evas_textblock_cursor_format_get(const Evas_Textblock_Cursor *cur)
8079 {
8080    if (!cur) return NULL;
8081    TB_NULL_CHECK(cur->node, NULL);
8082    return _evas_textblock_cursor_node_format_at_pos_get(cur);
8083 }
8084
8085 EAPI const char *
8086 evas_textblock_node_format_text_get(const Evas_Object_Textblock_Node_Format *fmt)
8087 {
8088    static char *ret = NULL;
8089    char *tmp;
8090
8091    if (!fmt) return NULL;
8092
8093    if (ret) free(ret);
8094    ret = malloc(strlen(fmt->orig_format) + 2 + 1);
8095    tmp = ret;
8096
8097    if (fmt->opener && !fmt->own_closer)
8098      {
8099         *(tmp++) = '+';
8100         *(tmp++) = ' ';
8101      }
8102    else if (!fmt->opener)
8103      {
8104         *(tmp++) = '-';
8105         *(tmp++) = ' ';
8106      }
8107    strcpy(tmp, fmt->orig_format);
8108    return ret;
8109 }
8110
8111 EAPI void
8112 evas_textblock_cursor_at_format_set(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *fmt)
8113 {
8114    if (!fmt || !cur) return;
8115    cur->node = fmt->text_node;
8116    cur->pos = _evas_textblock_node_format_pos_get(fmt);
8117 }
8118
8119 EAPI Eina_Bool
8120 evas_textblock_cursor_format_is_visible_get(const Evas_Textblock_Cursor *cur)
8121 {
8122    const Eina_Unicode *text;
8123
8124    if (!cur) return EINA_FALSE;
8125    TB_NULL_CHECK(cur->node, EINA_FALSE);
8126    if (!evas_textblock_cursor_is_format(cur)) return EINA_FALSE;
8127    text = eina_ustrbuf_string_get(cur->node->unicode);
8128    return EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(text[cur->pos]);
8129 }
8130
8131 EAPI int
8132 evas_textblock_cursor_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch, Evas_BiDi_Direction *dir, Evas_Textblock_Cursor_Type ctype)
8133 {
8134    int ret = -1;
8135    const Evas_Textblock_Cursor *dir_cur;
8136    Evas_Textblock_Cursor cur2;
8137    Evas_Object_Textblock *o;
8138    if (!cur) return -1;
8139    o = (Evas_Object_Textblock *)(cur->obj->object_data);
8140    if (!o->formatted.valid) _relayout(cur->obj);
8141
8142    dir_cur = cur;
8143    if (ctype == EVAS_TEXTBLOCK_CURSOR_UNDER)
8144      {
8145         ret = evas_textblock_cursor_pen_geometry_get(cur, cx, cy, cw, ch);
8146      }
8147    else if (ctype == EVAS_TEXTBLOCK_CURSOR_BEFORE)
8148      {
8149         /* In the case of a "before cursor", we should get the coordinates
8150          * of just after the previous char (which in bidi text may not be
8151          * just before the current char). */
8152         Evas_Coord x, y, h, w;
8153         Evas_Object_Textblock_Node_Format *fmt;
8154
8155         /* If it's at the end of the line, we want to get the position, not
8156          * the position of the previous */
8157         if ((cur->pos > 0) && !_evas_textblock_cursor_is_at_the_end(cur))
8158           {
8159 #ifdef BIDI_SUPPORT
8160              Eina_Bool before_char = EINA_FALSE;
8161 #endif
8162              cur2.obj = cur->obj;
8163              evas_textblock_cursor_copy(cur, &cur2);
8164              evas_textblock_cursor_char_prev(&cur2);
8165
8166              fmt = _evas_textblock_cursor_node_format_at_pos_get(&cur2);
8167
8168              if (!fmt || !_IS_LINE_SEPARATOR(fmt->format))
8169                {
8170                   dir_cur = &cur2;
8171 #ifdef BIDI_SUPPORT
8172                   before_char = EINA_FALSE;
8173 #endif
8174                }
8175 #ifdef BIDI_SUPPORT
8176              else
8177                {
8178                   before_char = EINA_TRUE;
8179                }
8180 #endif
8181              ret = evas_textblock_cursor_pen_geometry_get(
8182                    dir_cur, &x, &y, &w, &h);
8183 #ifdef BIDI_SUPPORT
8184              /* Adjust if the char is an rtl char */
8185              if (ret >= 0)
8186                {
8187                   Eina_Bool is_rtl = EINA_FALSE;
8188                   if (dir_cur->node->par->is_bidi)
8189                     {
8190                        Evas_Object_Textblock_Line *ln;
8191                        Evas_Object_Textblock_Item *it;
8192                        _find_layout_item_match(dir_cur, &ln, &it);
8193                        if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8194                              (_ITEM_TEXT(it)->text_props.bidi.dir ==
8195                               EVAS_BIDI_DIRECTION_RTL))
8196                           is_rtl = EINA_TRUE;
8197                        else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8198                              (_ITEM_FORMAT(it)->bidi_dir ==
8199                               EVAS_BIDI_DIRECTION_RTL))
8200                           is_rtl = EINA_TRUE;
8201                     }
8202
8203                   if ((!before_char && is_rtl) ||
8204                         (before_char && !is_rtl))
8205                     {
8206                        /* Just don't advance the width */
8207                        w = 0;
8208                     }
8209                }
8210 #endif
8211           }
8212         else if (cur->pos == 0)
8213           {
8214              ret = evas_textblock_cursor_pen_geometry_get(
8215                    dir_cur, &x, &y, &w, &h);
8216 #ifdef BIDI_SUPPORT
8217              Eina_Bool is_rtl = EINA_FALSE;
8218              if (dir_cur->node && dir_cur->node->par->is_bidi)
8219                {
8220                   Evas_Object_Textblock_Line *ln;
8221                   Evas_Object_Textblock_Item *it;
8222                   _find_layout_item_match(dir_cur, &ln, &it);
8223                   if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8224                         (_ITEM_TEXT(it)->text_props.bidi.dir ==
8225                          EVAS_BIDI_DIRECTION_RTL))
8226                      is_rtl = EINA_TRUE;
8227                   else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8228                         (_ITEM_FORMAT(it)->bidi_dir ==
8229                          EVAS_BIDI_DIRECTION_RTL))
8230                      is_rtl = EINA_TRUE;
8231                }
8232
8233              /* Adjust if the char is an rtl char */
8234              if ((ret >= 0) && (!is_rtl))
8235                {
8236                   /* Just don't advance the width */
8237                   w = 0;
8238                }
8239 #endif
8240           }
8241         else
8242           {
8243              ret = evas_textblock_cursor_pen_geometry_get(
8244                    dir_cur, &x, &y, &w, &h);
8245           }
8246         if (ret >= 0)
8247           {
8248              if (cx) *cx = x + w;
8249              if (cy) *cy = y;
8250              if (cw) *cw = 0;
8251              if (ch) *ch = h;
8252           }
8253      }
8254
8255    if (dir && dir_cur && dir_cur->node)
8256      {
8257 #ifdef BIDI_SUPPORT
8258         Eina_Bool is_rtl = EINA_FALSE;
8259         if (dir_cur->node->par->is_bidi)
8260           {
8261              Evas_Object_Textblock_Line *ln;
8262              Evas_Object_Textblock_Item *it;
8263              _find_layout_item_match(dir_cur, &ln, &it);
8264              if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8265                    (_ITEM_TEXT(it)->text_props.bidi.dir ==
8266                     EVAS_BIDI_DIRECTION_RTL))
8267                 is_rtl = EINA_TRUE;
8268              else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8269                    (_ITEM_FORMAT(it)->bidi_dir ==
8270                     EVAS_BIDI_DIRECTION_RTL))
8271                 is_rtl = EINA_TRUE;
8272           }
8273
8274         if (_evas_textblock_cursor_is_at_the_end(dir_cur) && (dir_cur->pos > 0))
8275           {
8276              *dir = (is_rtl) ?
8277                 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
8278           }
8279         else if (dir_cur->pos > 0)
8280           {
8281              *dir = (is_rtl) ?
8282                 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
8283           }
8284         else
8285 #endif
8286           {
8287              *dir = EVAS_BIDI_DIRECTION_LTR;
8288           }
8289      }
8290    return ret;
8291 }
8292
8293 /**
8294  * @internal
8295  * Returns the geometry/pen position (depending on query_func) of the char
8296  * at pos.
8297  *
8298  * @param cur the position of the char.
8299  * @param query_func the query function to use.
8300  * @param cx the x of the char (or pen_x in the case of pen position).
8301  * @param cy the y of the char.
8302  * @param cw the w of the char (or advance in the case pen position).
8303  * @param ch the h of the char.
8304  * @return line number of the char on success, -1 on error.
8305  */
8306 static int
8307 _evas_textblock_cursor_char_pen_geometry_common_get(int (*query_func) (void *data, Evas_Font_Set *font, const Evas_Text_Props *intl_props, int pos, int *cx, int *cy, int *cw, int *ch), const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8308 {
8309    Evas_Object_Textblock *o;
8310    Evas_Object_Textblock_Line *ln = NULL;
8311    Evas_Object_Textblock_Item *it = NULL;
8312    Evas_Object_Textblock_Text_Item *ti = NULL;
8313    Evas_Object_Textblock_Format_Item *fi = NULL;
8314    int x = 0, y = 0, w = 0, h = 0;
8315    int pos;
8316    Eina_Bool previous_format;
8317
8318    if (!cur) return -1;
8319    o = (Evas_Object_Textblock *)(cur->obj->object_data);
8320    if (!o->formatted.valid) _relayout(cur->obj);
8321
8322    if (!cur->node)
8323      {
8324         if (!o->text_nodes)
8325           {
8326              if (!o->paragraphs) return -1;
8327              ln = o->paragraphs->lines;
8328              if (!ln) return -1;
8329              if (cx) *cx = ln->x;
8330              if (cy) *cy = ln->par->y + ln->y;
8331              if (cw) *cw = ln->w;
8332              if (ch) *ch = ln->h;
8333              return ln->par->line_no + ln->line_no;
8334           }
8335         else
8336           return -1;
8337      }
8338
8339    previous_format = _find_layout_item_match(cur, &ln, &it);
8340    if (!it)
8341      {
8342         return -1;
8343      }
8344    if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8345      {
8346         ti = _ITEM_TEXT(it);
8347      }
8348    else
8349      {
8350         fi = _ITEM_FORMAT(it);
8351      }
8352
8353    if (ln && ti)
8354      {
8355         pos = cur->pos - ti->parent.text_pos;
8356
8357         if (pos < 0) pos = 0;
8358         if (ti->parent.format->font.font)
8359           {
8360              query_func(cur->ENDT,
8361                    ti->parent.format->font.font,
8362                    &ti->text_props,
8363                    pos,
8364                    &x, &y, &w, &h);
8365           }
8366
8367         x += ln->x + _ITEM(ti)->x;
8368
8369         if (x < ln->x)
8370           {
8371              x = ln->x;
8372           }
8373         y = ln->par->y + ln->y;
8374         h = ln->h;
8375      }
8376    else if (ln && fi)
8377      {
8378         if (previous_format)
8379           {
8380              if (_IS_LINE_SEPARATOR(fi->item))
8381                {
8382                   x = 0;
8383                   y = ln->par->y + ln->y + ln->h;
8384                }
8385              else
8386                {
8387 #ifdef BIDI_SUPPORT
8388                   if (ln->par->direction == EVAS_BIDI_DIRECTION_RTL)
8389                     {
8390                        x = ln->x;
8391                     }
8392                   else
8393 #endif
8394                     {
8395                        x = ln->x + ln->w;
8396                     }
8397                   y = ln->par->y + ln->y;
8398                }
8399              w = 0;
8400              h = ln->h;
8401           }
8402         else
8403           {
8404              x = ln->x + _ITEM(fi)->x;
8405              y = ln->par->y + ln->y;
8406              w = _ITEM(fi)->w;
8407              h = ln->h;
8408           }
8409      }
8410    else
8411      {
8412         return -1;
8413      }
8414    if (cx) *cx = x;
8415    if (cy) *cy = y;
8416    if (cw) *cw = w;
8417    if (ch) *ch = h;
8418    return ln->par->line_no + ln->line_no;
8419 }
8420
8421 EAPI int
8422 evas_textblock_cursor_char_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8423 {
8424    if (!cur) return -1;
8425    return _evas_textblock_cursor_char_pen_geometry_common_get(
8426          cur->ENFN->font_char_coords_get, cur, cx, cy, cw, ch);
8427 }
8428
8429 EAPI int
8430 evas_textblock_cursor_pen_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8431 {
8432    if (!cur) return -1;
8433    return _evas_textblock_cursor_char_pen_geometry_common_get(
8434          cur->ENFN->font_pen_coords_get, cur, cx, cy, cw, ch);
8435 }
8436
8437 EAPI int
8438 evas_textblock_cursor_line_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8439 {
8440    Evas_Object_Textblock *o;
8441    Evas_Object_Textblock_Line *ln = NULL;
8442    Evas_Object_Textblock_Item *it = NULL;
8443    int x, y, w, h;
8444
8445    if (!cur) return -1;
8446    o = (Evas_Object_Textblock *)(cur->obj->object_data);
8447    if (!o->formatted.valid) _relayout(cur->obj);
8448    if (!cur->node)
8449      {
8450         ln = o->paragraphs->lines;
8451      }
8452    else
8453      {
8454         _find_layout_item_match(cur, &ln, &it);
8455      }
8456    if (!ln) return -1;
8457    x = ln->x;
8458    y = ln->par->y + ln->y;
8459    w = ln->w;
8460    h = ln->h;
8461    if (cx) *cx = x;
8462    if (cy) *cy = y;
8463    if (cw) *cw = w;
8464    if (ch) *ch = h;
8465    return ln->par->line_no + ln->line_no;
8466 }
8467
8468 EAPI Eina_Bool
8469 evas_textblock_cursor_visible_range_get(Evas_Textblock_Cursor *start, Evas_Textblock_Cursor *end)
8470 {
8471    Evas *e;
8472    Evas_Coord cy, ch;
8473    Evas_Object *obj = start->obj;
8474    TB_HEAD_RETURN(EINA_FALSE);
8475    e = evas_object_evas_get(obj);
8476    cy = 0 - obj->cur.geometry.y;
8477    ch = e->viewport.h;
8478    evas_textblock_cursor_line_coord_set(start, cy);
8479    evas_textblock_cursor_line_coord_set(end, cy + ch);
8480    evas_textblock_cursor_line_char_last(end);
8481
8482    return EINA_TRUE;
8483 }
8484
8485 EAPI Eina_Bool
8486 evas_textblock_cursor_char_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord x, Evas_Coord y)
8487 {
8488    Evas_Object_Textblock *o;
8489    Evas_Object_Textblock_Paragraph *found_par;
8490    Evas_Object_Textblock_Line *ln;
8491    Evas_Object_Textblock_Item *it = NULL;
8492
8493    if (!cur) return EINA_FALSE;
8494    o = (Evas_Object_Textblock *)(cur->obj->object_data);
8495    if (!o->formatted.valid) _relayout(cur->obj);
8496    x += o->style_pad.l;
8497    y += o->style_pad.t;
8498
8499    found_par = _layout_find_paragraph_by_y(o, y);
8500    if (found_par)
8501      {
8502         _layout_paragraph_render(o, found_par);
8503         EINA_INLIST_FOREACH(found_par->lines, ln)
8504           {
8505              if (ln->par->y + ln->y > y) break;
8506              if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
8507                {
8508                   /* If before or after the line, go to start/end according
8509                    * to paragraph direction. */
8510                   if (x < ln->x)
8511                     {
8512                        cur->pos = ln->items->text_pos;
8513                        cur->node = found_par->text_node;
8514                        if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
8515                          {
8516                             evas_textblock_cursor_line_char_last(cur);
8517                          }
8518                        else
8519                          {
8520                             evas_textblock_cursor_line_char_first(cur);
8521                          }
8522                        return EINA_TRUE;
8523                     }
8524                   else if (x >= ln->x + ln->w)
8525                     {
8526                        cur->pos = ln->items->text_pos;
8527                        cur->node = found_par->text_node;
8528                        if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
8529                          {
8530                             evas_textblock_cursor_line_char_first(cur);
8531                          }
8532                        else
8533                          {
8534                             evas_textblock_cursor_line_char_last(cur);
8535                          }
8536                        return EINA_TRUE;
8537                     }
8538
8539                   EINA_INLIST_FOREACH(ln->items, it)
8540                     {
8541                        if (((it->x + ln->x) <= x) && (((it->x + ln->x) + it->adv) > x))
8542                          {
8543                             if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8544                               {
8545                                  int pos;
8546                                  int cx, cy, cw, ch;
8547                                  Evas_Object_Textblock_Text_Item *ti;
8548                                  ti = _ITEM_TEXT(it);
8549
8550                                  pos = -1;
8551                                  if (ti->parent.format->font.font)
8552                                    pos = cur->ENFN->font_char_at_coords_get(
8553                                          cur->ENDT,
8554                                          ti->parent.format->font.font,
8555                                          &ti->text_props,
8556                                          x - it->x - ln->x, 0,
8557                                          &cx, &cy, &cw, &ch);
8558                                  if (pos < 0)
8559                                    return EINA_FALSE;
8560                                  cur->pos = pos + it->text_pos;
8561                                  cur->node = it->text_node;
8562                                  return EINA_TRUE;
8563                               }
8564                             else
8565                               {
8566                                  Evas_Object_Textblock_Format_Item *fi;
8567                                  fi = _ITEM_FORMAT(it);
8568                                  cur->pos = fi->parent.text_pos;
8569                                  cur->node = found_par->text_node;
8570                                  return EINA_TRUE;
8571                               }
8572                          }
8573                     }
8574                }
8575           }
8576      }
8577    else if (o->paragraphs && (y >= o->paragraphs->y + o->formatted.h))
8578      {
8579         /* If we are after the last paragraph, use the last position in the
8580          * text. */
8581         evas_textblock_cursor_paragraph_last(cur);
8582         return EINA_TRUE;
8583      }
8584    else if (o->paragraphs && (y < o->paragraphs->y))
8585      {
8586         evas_textblock_cursor_paragraph_first(cur);
8587         return EINA_TRUE;
8588      }
8589
8590    return EINA_FALSE;
8591 }
8592
8593 EAPI int
8594 evas_textblock_cursor_line_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord y)
8595 {
8596    Evas_Object_Textblock *o;
8597    Evas_Object_Textblock_Paragraph *found_par;
8598    Evas_Object_Textblock_Line *ln;
8599
8600    if (!cur) return -1;
8601    o = (Evas_Object_Textblock *)(cur->obj->object_data);
8602    if (!o->formatted.valid) _relayout(cur->obj);
8603    y += o->style_pad.t;
8604
8605    found_par = _layout_find_paragraph_by_y(o, y);
8606
8607    if (found_par)
8608      {
8609         _layout_paragraph_render(o, found_par);
8610         EINA_INLIST_FOREACH(found_par->lines, ln)
8611           {
8612              if (ln->par->y + ln->y > y) break;
8613              if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
8614                {
8615                   evas_textblock_cursor_line_set(cur, ln->par->line_no +
8616                         ln->line_no);
8617                   return ln->par->line_no + ln->line_no;
8618                }
8619           }
8620      }
8621    else if (o->paragraphs && (y >= o->paragraphs->y + o->formatted.h))
8622      {
8623         int line_no = 0;
8624         /* If we are after the last paragraph, use the last position in the
8625          * text. */
8626         evas_textblock_cursor_paragraph_last(cur);
8627         if (cur->node && cur->node->par)
8628           {
8629              line_no = cur->node->par->line_no;
8630              if (cur->node->par->lines)
8631                {
8632                   line_no += ((Evas_Object_Textblock_Line *)
8633                         EINA_INLIST_GET(cur->node->par->lines)->last)->line_no;
8634                }
8635           }
8636         return line_no;
8637      }
8638    else if (o->paragraphs && (y < o->paragraphs->y))
8639      {
8640         int line_no = 0;
8641         evas_textblock_cursor_paragraph_first(cur);
8642         if (cur->node && cur->node->par)
8643           {
8644              line_no = cur->node->par->line_no;
8645           }
8646         return line_no;
8647      }
8648    return -1;
8649 }
8650
8651 /**
8652  * @internal
8653  * Updates x and w according to the text direction, position in text and
8654  * if it's a special case switch
8655  *
8656  * @param ti the text item we are working on
8657  * @param x the current x (we get) and the x we return
8658  * @param w the current w (we get) and the w we return
8659  * @param start if this is the first item or not
8660  * @param switch_items toogles item switching (rtl cases)
8661  */
8662 static void
8663 _evas_textblock_range_calc_x_w(const Evas_Object_Textblock_Item *it,
8664       Evas_Coord *x, Evas_Coord *w, Eina_Bool start, Eina_Bool switch_items)
8665 {
8666    if ((start && !switch_items) || (!start && switch_items))
8667      {
8668 #ifdef BIDI_SUPPORT
8669         if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8670             _ITEM_TEXT(it)->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8671             ||
8672             ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8673              _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
8674           {
8675              *w = *x + *w;
8676              *x = 0;
8677           }
8678         else
8679 #endif
8680           {
8681              *w = it->adv - *x;
8682           }
8683      }
8684    else
8685      {
8686 #ifdef BIDI_SUPPORT
8687         if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8688             _ITEM_TEXT(it)->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8689             ||
8690             ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8691              _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
8692           {
8693              *x = *x + *w;
8694              *w = it->adv - *x;
8695           }
8696         else
8697 #endif
8698           {
8699              *w = *x;
8700              *x = 0;
8701           }
8702      }
8703
8704 }
8705
8706 /**
8707  * @internal
8708  * Returns the geometry of the range in line ln. Cur1 is the start cursor,
8709  * cur2 is the end cursor, NULL means from the start or to the end accordingly.
8710  * Assumes that ln is valid, and that at least one of cur1 and cur2 is not NULL.
8711  *
8712  * @param ln the line to work on.
8713  * @param cur1 the start cursor
8714  * @param cur2 the end cursor
8715  * @return Returns the geometry of the range
8716  */
8717 static Eina_List *
8718 _evas_textblock_cursor_range_in_line_geometry_get(
8719       const Evas_Object_Textblock_Line *ln, const Evas_Textblock_Cursor *cur1,
8720       const Evas_Textblock_Cursor *cur2)
8721 {
8722    Evas_Object_Textblock_Item *it;
8723    Evas_Object_Textblock_Item *it1, *it2;
8724    Eina_List *rects = NULL;
8725    Evas_Textblock_Rectangle *tr;
8726    size_t start, end;
8727    Eina_Bool switch_items;
8728    const Evas_Textblock_Cursor *cur;
8729
8730    cur = (cur1) ? cur1 : cur2;
8731
8732    if (!cur) return NULL;
8733
8734    /* Find the first and last items */
8735    it1 = it2 = NULL;
8736    start = end = 0;
8737    EINA_INLIST_FOREACH(ln->items, it)
8738      {
8739         size_t item_len;
8740         item_len = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
8741            _ITEM_TEXT(it)->text_props.text_len
8742            : 1;
8743         if ((!cur1 || (cur1->pos < it->text_pos + item_len)) &&
8744               (!cur2 || (cur2->pos >= it->text_pos)))
8745           {
8746              if (!it1)
8747                {
8748                   it1 = it;
8749                   start = item_len; /* start stores the first item_len */
8750                }
8751              it2 = it;
8752              end = item_len; /* end stores the last item_len */
8753           }
8754      }
8755
8756    /* If we couldn't find even one item, return */
8757    if (!it1) return NULL;
8758
8759    /* If the first item is logically before or equal the second item
8760     * we have to set start and end differently than in the other case */
8761    if (it1->text_pos <= it2->text_pos)
8762      {
8763         start = (cur1) ? (cur1->pos - it1->text_pos) : 0;
8764         end = (cur2) ? (cur2->pos - it2->text_pos) : end;
8765         switch_items = EINA_FALSE;
8766      }
8767    else
8768      {
8769         start = (cur2) ? (cur2->pos - it1->text_pos) : start;
8770         end = (cur1) ? (cur1->pos - it2->text_pos) : 0;
8771         switch_items = EINA_TRUE;
8772      }
8773
8774    /* IMPORTANT: Don't use cur1/cur2 past this point (because they probably
8775     * don't make sense anymore. That's why there are start and end),
8776     * unless you know what you are doing */
8777
8778    /* Special case when they share the same item and it's a text item */
8779    if ((it1 == it2) && (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT))
8780      {
8781         Evas_Coord x1, w1, x2, w2;
8782         Evas_Coord x, w, y, h;
8783         Evas_Object_Textblock_Text_Item *ti;
8784         int ret = 0;
8785
8786         ti = _ITEM_TEXT(it1);
8787         if (ti->parent.format->font.font)
8788           {
8789              ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8790                    ti->parent.format->font.font,
8791                    &ti->text_props,
8792                    start,
8793                    &x1, &y, &w1, &h);
8794           }
8795         if (!ret)
8796           {
8797              return NULL;
8798           }
8799         ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8800               ti->parent.format->font.font,
8801               &ti->text_props,
8802               end,
8803               &x2, &y, &w2, &h);
8804         if (!ret)
8805           {
8806              return NULL;
8807           }
8808
8809         /* Make x2 the one on the right */
8810         if (x2 < x1)
8811           {
8812              Evas_Coord tmp;
8813              tmp = x1;
8814              x1 = x2;
8815              x2 = tmp;
8816
8817              tmp = w1;
8818              w1 = w2;
8819              w2 = tmp;
8820           }
8821
8822 #ifdef BIDI_SUPPORT
8823         if (ti->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8824           {
8825              x = x1 + w1;
8826              w = x2 + w2 - x;
8827           }
8828         else
8829 #endif
8830           {
8831              x = x1;
8832              w = x2 - x1;
8833           }
8834         if (w > 0)
8835           {
8836              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8837              rects = eina_list_append(rects, tr);
8838              tr->x = ln->x + it1->x + x;
8839              tr->y = ln->par->y + ln->y;
8840              tr->h = ln->h;
8841              tr->w = w;
8842           }
8843      }
8844    else if (it1 != it2)
8845      {
8846         /* Get the middle items */
8847         Evas_Coord min_x, max_x;
8848         Evas_Coord x, w;
8849         it = _ITEM(EINA_INLIST_GET(it1)->next);
8850         min_x = max_x = it->x;
8851
8852         if (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8853           {
8854              Evas_Coord y, h;
8855              Evas_Object_Textblock_Text_Item *ti;
8856              int ret;
8857              ti = _ITEM_TEXT(it1);
8858
8859              ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8860                    ti->parent.format->font.font,
8861                    &ti->text_props,
8862                    start,
8863                    &x, &y, &w, &h);
8864              if (!ret)
8865                {
8866                   /* BUG! Skip the first item */
8867                   x = w = 0;
8868                }
8869              else
8870                {
8871                   _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
8872                         switch_items);
8873                }
8874           }
8875         else
8876           {
8877              x = 0;
8878              w = it1->w;
8879              _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
8880                    switch_items);
8881           }
8882         if (w > 0)
8883           {
8884              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8885              rects = eina_list_append(rects, tr);
8886              tr->x = ln->x + it1->x + x;
8887              tr->y = ln->par->y + ln->y;
8888              tr->h = ln->h;
8889              tr->w = w;
8890           }
8891
8892         while (it && (it != it2))
8893           {
8894              max_x = it->x + it->adv;
8895              it = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(it)->next;
8896           }
8897         if (min_x != max_x)
8898           {
8899              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8900              rects = eina_list_append(rects, tr);
8901              tr->x = ln->x + min_x;
8902              tr->y = ln->par->y + ln->y;
8903              tr->h = ln->h;
8904              tr->w = max_x - min_x;
8905           }
8906         if (it2->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8907           {
8908              Evas_Coord y, h;
8909              Evas_Object_Textblock_Text_Item *ti;
8910              int ret;
8911              ti = _ITEM_TEXT(it2);
8912
8913              ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8914                    ti->parent.format->font.font,
8915                    &ti->text_props,
8916                    end,
8917                    &x, &y, &w, &h);
8918              if (!ret)
8919                {
8920                   /* BUG! skip the last item */
8921                   x = w = 0;
8922                }
8923              else
8924                {
8925                   _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
8926                         switch_items);
8927                }
8928           }
8929         else
8930           {
8931              x = 0;
8932              w = it2->w;
8933              _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
8934                         switch_items);
8935           }
8936         if (w > 0)
8937           {
8938              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8939              rects = eina_list_append(rects, tr);
8940              tr->x = ln->x + it2->x + x;
8941              tr->y = ln->par->y + ln->y;
8942              tr->h = ln->h;
8943              tr->w = w;
8944           }
8945      }
8946    return rects;
8947 }
8948
8949 EAPI Eina_List *
8950 evas_textblock_cursor_range_geometry_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
8951 {
8952    Evas_Object_Textblock *o;
8953    Evas_Object_Textblock_Line *ln1, *ln2;
8954    Evas_Object_Textblock_Item *it1, *it2;
8955    Eina_List *rects = NULL;
8956    Evas_Textblock_Rectangle *tr;
8957
8958    if (!cur1 || !cur1->node) return NULL;
8959    if (!cur2 || !cur2->node) return NULL;
8960    if (cur1->obj != cur2->obj) return NULL;
8961    o = (Evas_Object_Textblock *)(cur1->obj->object_data);
8962    if (!o->formatted.valid) _relayout(cur1->obj);
8963    if (evas_textblock_cursor_compare(cur1, cur2) > 0)
8964      {
8965         const Evas_Textblock_Cursor *tc;
8966
8967         tc = cur1;
8968         cur1 = cur2;
8969         cur2 = tc;
8970      }
8971
8972    ln1 = ln2 = NULL;
8973    it1 = it2 = NULL;
8974    _find_layout_item_match(cur1, &ln1, &it1);
8975    if (!ln1 || !it1) return NULL;
8976    _find_layout_item_match(cur2, &ln2, &it2);
8977    if (!ln2 || !it2) return NULL;
8978
8979    if (ln1 == ln2)
8980      {
8981         rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
8982               cur1, cur2);
8983      }
8984    else
8985      {
8986         Evas_Object_Textblock_Line *plni, *lni;
8987         Eina_List *rects2 = NULL;
8988         /* Handle the first line */
8989         rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
8990               cur1, NULL);
8991
8992         /* Handle the lines between the first and the last line */
8993         lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(ln1)->next;
8994         if (!lni && (ln1->par != ln2->par))
8995           {
8996              lni = ((Evas_Object_Textblock_Paragraph *)
8997                     EINA_INLIST_GET(ln1->par)->next)->lines;
8998           }
8999         while (lni && (lni != ln2))
9000           {
9001              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
9002              rects = eina_list_append(rects, tr);
9003              tr->x = lni->x;
9004              tr->y = lni->par->y + lni->y;
9005              tr->h = lni->h;
9006              tr->w = lni->w;
9007              plni = lni;
9008              lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(lni)->next;
9009              if (!lni && (plni->par != ln2->par))
9010                {
9011                   lni = ((Evas_Object_Textblock_Paragraph *)
9012                      EINA_INLIST_GET(plni->par)->next)->lines;
9013                }
9014           }
9015         rects2 = _evas_textblock_cursor_range_in_line_geometry_get(ln2,
9016               NULL, cur2);
9017         rects = eina_list_merge(rects, rects2);
9018      }
9019    return rects;
9020 }
9021
9022 EAPI Eina_Bool
9023 evas_textblock_cursor_format_item_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
9024 {
9025    Evas_Object_Textblock *o;
9026    Evas_Object_Textblock_Line *ln = NULL;
9027    Evas_Object_Textblock_Format_Item *fi;
9028    Evas_Object_Textblock_Item *it = NULL;
9029    Evas_Coord x, y, w, h;
9030
9031    if (!cur || !evas_textblock_cursor_format_is_visible_get(cur)) return EINA_FALSE;
9032    o = (Evas_Object_Textblock *)(cur->obj->object_data);
9033    if (!o->formatted.valid) _relayout(cur->obj);
9034    if (!evas_textblock_cursor_format_is_visible_get(cur)) return EINA_FALSE;
9035    _find_layout_item_line_match(cur->obj, cur->node, cur->pos, &ln, &it);
9036    fi = _ITEM_FORMAT(it);
9037    if ((!ln) || (!fi)) return EINA_FALSE;
9038    x = ln->x + fi->parent.x;
9039    y = ln->par->y + ln->y + ln->baseline + fi->y;
9040    w = fi->parent.w;
9041    h = fi->parent.h;
9042    if (cx) *cx = x;
9043    if (cy) *cy = y;
9044    if (cw) *cw = w;
9045    if (ch) *ch = h;
9046    return EINA_TRUE;
9047 }
9048
9049 EAPI Eina_Bool
9050 evas_textblock_cursor_eol_get(const Evas_Textblock_Cursor *cur)
9051 {
9052    Eina_Bool ret = EINA_FALSE;
9053    Evas_Textblock_Cursor cur2;
9054    if (!cur) return EINA_FALSE;
9055
9056    cur2.obj = cur->obj;
9057    evas_textblock_cursor_copy(cur, &cur2);
9058    evas_textblock_cursor_line_char_last(&cur2);
9059    if (cur2.pos == cur->pos)
9060      {
9061         ret = EINA_TRUE;
9062      }
9063    return ret;
9064 }
9065
9066 /* general controls */
9067 EAPI Eina_Bool
9068 evas_object_textblock_line_number_geometry_get(const Evas_Object *obj, int line, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
9069 {
9070    Evas_Object_Textblock_Line *ln;
9071
9072    TB_HEAD_RETURN(0);
9073    ln = _find_layout_line_num(obj, line);
9074    if (!ln) return EINA_FALSE;
9075    if (cx) *cx = ln->x;
9076    if (cy) *cy = ln->par->y + ln->y;
9077    if (cw) *cw = ln->w;
9078    if (ch) *ch = ln->h;
9079    return EINA_TRUE;
9080 }
9081
9082 static void
9083 _evas_object_textblock_clear_all(Evas_Object *obj)
9084 {
9085    Eina_List *l;
9086    Evas_Textblock_Cursor *cur;
9087
9088    TB_HEAD();
9089    if (o->paragraphs)
9090      {
9091         _paragraphs_free(obj, o->paragraphs);
9092         o->paragraphs = NULL;
9093      }
9094
9095    _nodes_clear(obj);
9096    o->cursor->node = NULL;
9097    o->cursor->pos = 0;
9098    EINA_LIST_FOREACH(o->cursors, l, cur)
9099      {
9100         cur->node = NULL;
9101         cur->pos = 0;
9102
9103      }
9104
9105    _evas_textblock_changed(o, obj);
9106 }
9107
9108 EAPI void
9109 evas_object_textblock_clear(Evas_Object *obj)
9110 {
9111    TB_HEAD();
9112    _evas_object_textblock_clear_all(obj);
9113
9114    /* Force recreation of everything for textblock.
9115     * FIXME: We have the same thing in other places, merge it... */
9116    evas_textblock_cursor_paragraph_first(o->cursor);
9117    evas_textblock_cursor_text_append(o->cursor, "");
9118 }
9119
9120 EAPI void
9121 evas_object_textblock_size_formatted_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
9122 {
9123    TB_HEAD();
9124    if (!o->formatted.valid) _relayout(obj);
9125    if (w) *w = o->formatted.w;
9126    if (h) *h = o->formatted.h;
9127 }
9128
9129 static void
9130 _size_native_calc_line_finalize(const Evas_Object *obj, Eina_List *items,
9131       Evas_Coord *ascent, Evas_Coord *descent, Evas_Coord *w)
9132 {
9133    Evas_Object_Textblock_Item *it;
9134    Eina_List *i;
9135
9136    it = eina_list_data_get(items);
9137    *w = 0;
9138
9139    if (it)
9140      {
9141         /* If there are no text items yet, calc ascent/descent
9142          * according to the current format. */
9143         if (*ascent + *descent == 0)
9144            _layout_format_ascent_descent_adjust(obj, ascent, descent,
9145                  it->format);
9146
9147         /* Add margins. */
9148         if (it->format)
9149            *w = it->format->margin.l + it->format->margin.r;
9150       }
9151
9152
9153    /* Adjust all the item sizes according to the final line size,
9154     * and update the x positions of all the items of the line. */
9155    EINA_LIST_FOREACH(items, i, it)
9156      {
9157         if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
9158           {
9159              Evas_Coord fw, fh, fy;
9160
9161              Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
9162              if (!fi->formatme) goto loop_advance;
9163              _layout_calculate_format_item_size(obj, fi, ascent,
9164                    descent, &fy, &fw, &fh);
9165           }
9166
9167 loop_advance:
9168         *w += it->adv;
9169      }
9170 }
9171
9172 /* FIXME: doc */
9173 static void
9174 _size_native_calc_paragraph_size(const Evas_Object *obj,
9175       const Evas_Object_Textblock *o,
9176       const Evas_Object_Textblock_Paragraph *par,
9177       Evas_Coord *_w, Evas_Coord *_h)
9178 {
9179    Eina_List *i;
9180    Evas_Object_Textblock_Item *it;
9181    Eina_List *line_items = NULL;
9182    Evas_Coord w = 0, y = 0, wmax = 0, h = 0, ascent = 0, descent = 0;
9183
9184    EINA_LIST_FOREACH(par->logical_items, i, it)
9185      {
9186         line_items = eina_list_append(line_items, it);
9187         if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
9188           {
9189              Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
9190              if (fi->item && (_IS_LINE_SEPARATOR(fi->item) ||
9191                       _IS_PARAGRAPH_SEPARATOR(o, fi->item)))
9192                {
9193                   _size_native_calc_line_finalize(obj, line_items, &ascent,
9194                         &descent, &w);
9195
9196                   if (ascent + descent > h)
9197                      h = ascent + descent;
9198
9199                   y += h;
9200                   if (w > wmax)
9201                      wmax = w;
9202                   h = 0;
9203                   ascent = descent = 0;
9204                   line_items = eina_list_free(line_items);
9205                }
9206              else
9207                {
9208                   Evas_Coord fw, fh, fy;
9209                   /* If there are no text items yet, calc ascent/descent
9210                    * according to the current format. */
9211                   if (it && (ascent + descent == 0))
9212                      _layout_format_ascent_descent_adjust(obj, &ascent,
9213                            &descent, it->format);
9214
9215                   _layout_calculate_format_item_size(obj, fi, &ascent,
9216                         &descent, &fy, &fw, &fh);
9217                }
9218           }
9219         else
9220           {
9221              _layout_format_ascent_descent_adjust(obj, &ascent,
9222                    &descent, it->format);
9223           }
9224      }
9225
9226    _size_native_calc_line_finalize(obj, line_items, &ascent, &descent, &w);
9227
9228    line_items = eina_list_free(line_items);
9229
9230    /* Do the last addition */
9231    if (ascent + descent > h)
9232       h = ascent + descent;
9233
9234    if (w > wmax)
9235       wmax = w;
9236
9237    *_h = y + h;
9238    *_w = wmax;
9239 }
9240
9241 EAPI void
9242 evas_object_textblock_size_native_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
9243 {
9244    TB_HEAD();
9245    if (!o->native.valid)
9246      {
9247         Evas_Coord wmax = 0, hmax = 0;
9248         Evas_Object_Textblock_Paragraph *par;
9249         /* We just want the layout objects to update, should probably
9250          * split that. */
9251         if (!o->formatted.valid) _relayout(obj);
9252         EINA_INLIST_FOREACH(o->paragraphs, par)
9253           {
9254              Evas_Coord tw, th;
9255              _size_native_calc_paragraph_size(obj, o, par, &tw, &th);
9256              if (tw > wmax)
9257                 wmax = tw;
9258              hmax += th;
9259           }
9260
9261         o->native.w = wmax;
9262         o->native.h = hmax;
9263
9264         o->native.valid = 1;
9265         o->content_changed = 0;
9266         o->format_changed = EINA_FALSE;
9267      }
9268    if (w) *w = o->native.w;
9269    if (h) *h = o->native.h;
9270 }
9271
9272 EAPI void
9273 evas_object_textblock_style_insets_get(const Evas_Object *obj, Evas_Coord *l, Evas_Coord *r, Evas_Coord *t, Evas_Coord *b)
9274 {
9275    TB_HEAD();
9276    if (!o->formatted.valid) _relayout(obj);
9277    if (l) *l = o->style_pad.l;
9278    if (r) *r = o->style_pad.r;
9279    if (t) *t = o->style_pad.t;
9280    if (b) *b = o->style_pad.b;
9281 }
9282
9283 /** @internal
9284  * FIXME: DELETE ME! DELETE ME!
9285  * This is an ugly workaround to get around the fact that
9286  * evas_object_textblock_coords_recalc isn't really called when it's supposed
9287  * to. When that bug is fixed please remove this. */
9288 static void
9289 _workaround_object_coords_recalc(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
9290 {
9291    evas_object_textblock_coords_recalc(obj);
9292 }
9293
9294 /* all nice and private */
9295 static void
9296 evas_object_textblock_init(Evas_Object *obj)
9297 {
9298    Evas_Object_Textblock *o;
9299 #ifdef HAVE_LINEBREAK
9300    static Eina_Bool linebreak_init = EINA_FALSE;
9301    if (!linebreak_init)
9302      {
9303         linebreak_init = EINA_TRUE;
9304         init_linebreak();
9305         init_wordbreak();
9306      }
9307 #endif
9308
9309    /* alloc image ob, setup methods and default values */
9310    obj->object_data = evas_object_textblock_new();
9311    /* set up default settings for this kind of object */
9312    obj->cur.color.r = 255;
9313    obj->cur.color.g = 255;
9314    obj->cur.color.b = 255;
9315    obj->cur.color.a = 255;
9316    obj->cur.geometry.x = 0.0;
9317    obj->cur.geometry.y = 0.0;
9318    obj->cur.geometry.w = 0.0;
9319    obj->cur.geometry.h = 0.0;
9320    obj->cur.layer = 0;
9321    /* set up object-specific settings */
9322    obj->prev = obj->cur;
9323    /* set up methods (compulsory) */
9324    obj->func = &object_func;
9325    obj->type = o_type;
9326
9327    o = (Evas_Object_Textblock *)(obj->object_data);
9328    o->cursor->obj = obj;
9329    evas_object_textblock_text_markup_set(obj, "");
9330
9331    o->legacy_newline = EINA_TRUE;
9332    evas_object_event_callback_priority_add(obj, EVAS_CALLBACK_RESIZE, -1000,
9333          _workaround_object_coords_recalc, NULL);
9334 }
9335
9336 static void *
9337 evas_object_textblock_new(void)
9338 {
9339    Evas_Object_Textblock *o;
9340
9341    /* alloc obj private data */
9342    EVAS_MEMPOOL_INIT(_mp_obj, "evas_object_textblock", Evas_Object_Textblock, 8, NULL);
9343    o = EVAS_MEMPOOL_ALLOC(_mp_obj, Evas_Object_Textblock);
9344    if (!o) return NULL;
9345    EVAS_MEMPOOL_PREP(_mp_obj, o, Evas_Object_Textblock);
9346    o->magic = MAGIC_OBJ_TEXTBLOCK;
9347    o->cursor = calloc(1, sizeof(Evas_Textblock_Cursor));
9348    _format_command_init();
9349    return o;
9350 }
9351
9352 static void
9353 evas_object_textblock_free(Evas_Object *obj)
9354 {
9355    Evas_Object_Textblock *o;
9356
9357    _evas_object_textblock_clear_all(obj);
9358    evas_object_textblock_style_set(obj, NULL);
9359    while (evas_object_textblock_style_user_peek(obj))
9360      {
9361         evas_object_textblock_style_user_pop(obj);
9362      }
9363    o = (Evas_Object_Textblock *)(obj->object_data);
9364    free(o->cursor);
9365    while (o->cursors)
9366      {
9367         Evas_Textblock_Cursor *cur;
9368
9369         cur = (Evas_Textblock_Cursor *)o->cursors->data;
9370         o->cursors = eina_list_remove_list(o->cursors, o->cursors);
9371         free(cur);
9372      }
9373    if (o->repch) eina_stringshare_del(o->repch);
9374    if (o->ellip_ti) _item_free(obj, NULL, _ITEM(o->ellip_ti));
9375    o->magic = 0;
9376    EVAS_MEMPOOL_FREE(_mp_obj, o);
9377   _format_command_shutdown();
9378 }
9379
9380
9381 static void
9382 evas_object_textblock_render(Evas_Object *obj, void *output, void *context, void *surface, int x, int y)
9383 {
9384    Evas_Object_Textblock_Paragraph *par, *start = NULL;
9385    Evas_Object_Textblock_Line *ln;
9386    Evas_Object_Textblock *o;
9387    int i, j;
9388    int cx, cy, cw, ch, clip;
9389    const char vals[5][5] =
9390      {
9391           {0, 1, 2, 1, 0},
9392           {1, 3, 4, 3, 1},
9393           {2, 4, 5, 4, 2},
9394           {1, 3, 4, 3, 1},
9395           {0, 1, 2, 1, 0}
9396      };
9397
9398    /* render object to surface with context, and offxet by x,y */
9399    o = (Evas_Object_Textblock *)(obj->object_data);
9400    obj->layer->evas->engine.func->context_multiplier_unset(output,
9401                                                            context);
9402    /* FIXME: This clipping is just until we fix inset handling correctly. */
9403    ENFN->context_clip_clip(output, context,
9404                               obj->cur.geometry.x + x,
9405                               obj->cur.geometry.y + y,
9406                               obj->cur.geometry.w,
9407                               obj->cur.geometry.h);
9408    clip = ENFN->context_clip_get(output, context, &cx, &cy, &cw, &ch);
9409    /* If there are no paragraphs and thus there are no lines,
9410     * there's nothing left to do. */
9411    if (!o->paragraphs) return;
9412
9413 #define ITEM_WALK() \
9414    EINA_INLIST_FOREACH(start, par) \
9415      { \
9416         if (!par->visible) continue; \
9417         if (clip) \
9418           { \
9419              if ((obj->cur.geometry.y + y + par->y + par->h) < (cy - 20)) \
9420              continue; \
9421              if ((obj->cur.geometry.y + y + par->y) > (cy + ch + 20)) \
9422              break; \
9423           } \
9424         _layout_paragraph_render(o, par); \
9425         EINA_INLIST_FOREACH(par->lines, ln) \
9426           { \
9427              Evas_Object_Textblock_Item *itr; \
9428              \
9429              if (clip) \
9430                { \
9431                   if ((obj->cur.geometry.y + y + par->y + ln->y + ln->h) < (cy - 20)) \
9432                   continue; \
9433                   if ((obj->cur.geometry.y + y + par->y + ln->y) > (cy + ch + 20)) \
9434                   break; \
9435                } \
9436              EINA_INLIST_FOREACH(ln->items, itr) \
9437                { \
9438                   Evas_Coord yoff; \
9439                   yoff = ln->baseline; \
9440                   if (itr->format->valign != -1.0) \
9441                     { \
9442                        yoff += itr->format->valign * (ln->h - itr->h); \
9443                     } \
9444                   if (clip) \
9445                     { \
9446                        if ((obj->cur.geometry.x + x + ln->x + itr->x + itr->w) < (cx - 20)) \
9447                        continue; \
9448                        if ((obj->cur.geometry.x + x + ln->x + itr->x) > (cx + cw + 20)) \
9449                        break; \
9450                     } \
9451                   if ((ln->x + itr->x + itr->w) <= 0) continue; \
9452                   if (ln->x + itr->x > obj->cur.geometry.w) break; \
9453                   do
9454
9455 #define ITEM_WALK_END() \
9456                   while (0); \
9457                } \
9458           } \
9459      } \
9460    do {} while(0)
9461 #define COLOR_SET(col) \
9462    ENFN->context_color_set(output, context, \
9463          (obj->cur.cache.clip.r * ti->parent.format->color.col.r) / 255, \
9464          (obj->cur.cache.clip.g * ti->parent.format->color.col.g) / 255, \
9465          (obj->cur.cache.clip.b * ti->parent.format->color.col.b) / 255, \
9466          (obj->cur.cache.clip.a * ti->parent.format->color.col.a) / 255);
9467 #define COLOR_SET_AMUL(col, amul) \
9468    ENFN->context_color_set(output, context, \
9469          (obj->cur.cache.clip.r * ti->parent.format->color.col.r * (amul)) / 65025, \
9470          (obj->cur.cache.clip.g * ti->parent.format->color.col.g * (amul)) / 65025, \
9471          (obj->cur.cache.clip.b * ti->parent.format->color.col.b * (amul)) / 65025, \
9472          (obj->cur.cache.clip.a * ti->parent.format->color.col.a * (amul)) / 65025);
9473 #define DRAW_TEXT(ox, oy) \
9474    if (ti->parent.format->font.font) ENFN->font_draw(output, context, surface, ti->parent.format->font.font, \
9475          obj->cur.geometry.x + ln->x + ti->parent.x + x + (ox), \
9476          obj->cur.geometry.y + ln->par->y + ln->y + yoff + y + (oy), \
9477          ti->parent.w, ti->parent.h, ti->parent.w, ti->parent.h, \
9478          &ti->text_props);
9479
9480    /* backing */
9481 #define DRAW_RECT(ox, oy, ow, oh, or, og, ob, oa) \
9482    do \
9483      { \
9484         ENFN->context_color_set(output, \
9485               context, \
9486               (obj->cur.cache.clip.r * or) / 255, \
9487               (obj->cur.cache.clip.g * og) / 255, \
9488               (obj->cur.cache.clip.b * ob) / 255, \
9489               (obj->cur.cache.clip.a * oa) / 255); \
9490         ENFN->rectangle_draw(output, \
9491               context, \
9492               surface, \
9493               obj->cur.geometry.x + ln->x + x + (ox), \
9494               obj->cur.geometry.y + ln->par->y + ln->y + y + (oy), \
9495               (ow), \
9496               (oh)); \
9497      } \
9498    while (0)
9499
9500 #define DRAW_FORMAT_DASHED(oname, oy, oh, dw, dp) \
9501    do \
9502      { \
9503         if (itr->format->oname) \
9504           { \
9505              unsigned char _or, _og, _ob, _oa; \
9506              int _ind, _dx = 0, _dn, _dr; \
9507              _or = itr->format->color.oname.r; \
9508              _og = itr->format->color.oname.g; \
9509              _ob = itr->format->color.oname.b; \
9510              _oa = itr->format->color.oname.a; \
9511              if (!EINA_INLIST_GET(itr)->next) \
9512                { \
9513                   _dn = itr->w / (dw + dp); \
9514                   _dr = itr->w % (dw + dp); \
9515                } \
9516              else \
9517                { \
9518                   _dn = itr->adv / (dw + dp); \
9519                   _dr = itr->adv % (dw + dp); \
9520                } \
9521              if (_dr > dw) _dr = dw; \
9522              for (_ind = 0 ; _ind < _dn ; _ind++) \
9523                { \
9524                   DRAW_RECT(itr->x + _dx, oy, dw, oh, _or, _og, _ob, _oa); \
9525                   _dx += dw + dp; \
9526                } \
9527              DRAW_RECT(itr->x + _dx, oy, _dr, oh, _or, _og, _ob, _oa); \
9528           } \
9529      } \
9530    while (0)
9531
9532 #define DRAW_FORMAT(oname, oy, oh) \
9533    do \
9534      { \
9535         if (itr->format->oname) \
9536           { \
9537              unsigned char _or, _og, _ob, _oa; \
9538              _or = itr->format->color.oname.r; \
9539              _og = itr->format->color.oname.g; \
9540              _ob = itr->format->color.oname.b; \
9541              _oa = itr->format->color.oname.a; \
9542              if (!EINA_INLIST_GET(itr)->next) \
9543                { \
9544                   DRAW_RECT(itr->x, oy, itr->w, oh, _or, _og, _ob, _oa); \
9545                } \
9546              else \
9547                { \
9548                   DRAW_RECT(itr->x, oy, itr->adv, oh, _or, _og, _ob, _oa); \
9549                } \
9550           } \
9551      } \
9552    while (0)
9553
9554      {
9555         Evas_Coord look_for_y = 0 - (obj->cur.geometry.y + y);
9556         if (clip)
9557           {
9558              Evas_Coord tmp_lfy = cy - (obj->cur.geometry.y + y);
9559              if (tmp_lfy > look_for_y)
9560                 look_for_y = tmp_lfy;
9561           }
9562
9563         if (look_for_y >= 0)
9564            start = _layout_find_paragraph_by_y(o, look_for_y);
9565
9566         if (!start)
9567            start = o->paragraphs;
9568      }
9569
9570    ITEM_WALK()
9571      {
9572         DRAW_FORMAT(backing, 0, ln->h);
9573      }
9574    ITEM_WALK_END();
9575
9576    /* There are size adjustments that depend on the styles drawn here back
9577     * in "_text_item_update_sizes" should not modify one without the other. */
9578
9579    /* prepare everything for text draw */
9580
9581    /* shadows */
9582    ITEM_WALK()
9583      {
9584         int shad_dst, shad_sz, dx, dy, haveshad;
9585         Evas_Object_Textblock_Text_Item *ti;
9586         ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9587         if (!ti) continue;
9588
9589         shad_dst = shad_sz = dx = dy = haveshad = 0;
9590         switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC)
9591           {
9592            case EVAS_TEXT_STYLE_SHADOW:
9593            case EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW:
9594              shad_dst = 1;
9595              haveshad = 1;
9596              break;
9597            case EVAS_TEXT_STYLE_OUTLINE_SHADOW:
9598            case EVAS_TEXT_STYLE_FAR_SHADOW:
9599              shad_dst = 2;
9600              haveshad = 1;
9601              break;
9602            case EVAS_TEXT_STYLE_FAR_SOFT_SHADOW:
9603              shad_dst = 2;
9604              shad_sz = 2;
9605              haveshad = 1;
9606              break;
9607            case EVAS_TEXT_STYLE_SOFT_SHADOW:
9608              shad_dst = 1;
9609              shad_sz = 2;
9610              haveshad = 1;
9611              break;
9612            default:
9613              break;
9614           }
9615         if (haveshad)
9616           {
9617              if (shad_dst > 0)
9618                {
9619                   switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_SHADOW_DIRECTION)
9620                     {
9621                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT:
9622                        dx = 1;
9623                        dy = 1;
9624                        break;
9625                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM:
9626                        dx = 0;
9627                        dy = 1;
9628                        break;
9629                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT:
9630                        dx = -1;
9631                        dy = 1;
9632                        break;
9633                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT:
9634                        dx = -1;
9635                        dy = 0;
9636                        break;
9637                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT:
9638                        dx = -1;
9639                        dy = -1;
9640                        break;
9641                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP:
9642                        dx = 0;
9643                        dy = -1;
9644                        break;
9645                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT:
9646                        dx = 1;
9647                        dy = -1;
9648                        break;
9649                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT:
9650                        dx = 1;
9651                        dy = 0;
9652                      default:
9653                        break;
9654                     }
9655                   dx *= shad_dst;
9656                   dy *= shad_dst;
9657                }
9658              switch (shad_sz)
9659                {
9660                 case 0:
9661                   COLOR_SET(shadow);
9662                   DRAW_TEXT(dx, dy);
9663                   break;
9664                 case 2:
9665                   for (j = 0; j < 5; j++)
9666                     {
9667                        for (i = 0; i < 5; i++)
9668                          {
9669                             if (vals[i][j] != 0)
9670                               {
9671                                  COLOR_SET_AMUL(shadow, vals[i][j] * 50);
9672                                  DRAW_TEXT(i - 2 + dx, j - 2 + dy);
9673                               }
9674                          }
9675                     }
9676                   break;
9677                 default:
9678                   break;
9679                }
9680           }
9681      }
9682    ITEM_WALK_END();
9683
9684    /* glows */
9685    ITEM_WALK()
9686      {
9687         Evas_Object_Textblock_Text_Item *ti;
9688         ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9689         if (!ti) continue;
9690
9691         if (ti->parent.format->style == EVAS_TEXT_STYLE_GLOW)
9692           {
9693              for (j = 0; j < 5; j++)
9694                {
9695                   for (i = 0; i < 5; i++)
9696                     {
9697                        if (vals[i][j] != 0)
9698                          {
9699                             COLOR_SET_AMUL(glow, vals[i][j] * 50);
9700                             DRAW_TEXT(i - 2, j - 2);
9701                          }
9702                     }
9703                }
9704              COLOR_SET(glow2);
9705              DRAW_TEXT(-1, 0);
9706              DRAW_TEXT(1, 0);
9707              DRAW_TEXT(0, -1);
9708              DRAW_TEXT(0, 1);
9709           }
9710      }
9711    ITEM_WALK_END();
9712
9713    /* outlines */
9714    ITEM_WALK()
9715      {
9716         Evas_Object_Textblock_Text_Item *ti;
9717         ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9718         if (!ti) continue;
9719
9720         if ((ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE) ||
9721               (ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE_SHADOW) ||
9722               (ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW))
9723           {
9724              COLOR_SET(outline);
9725              DRAW_TEXT(-1, 0);
9726              DRAW_TEXT(1, 0);
9727              DRAW_TEXT(0, -1);
9728              DRAW_TEXT(0, 1);
9729           }
9730         else if (ti->parent.format->style == EVAS_TEXT_STYLE_SOFT_OUTLINE)
9731           {
9732              for (j = 0; j < 5; j++)
9733                {
9734                   for (i = 0; i < 5; i++)
9735                     {
9736                        if (((i != 2) || (j != 2)) && (vals[i][j] != 0))
9737                          {
9738                             COLOR_SET_AMUL(outline, vals[i][j] * 50);
9739                             DRAW_TEXT(i - 2, j - 2);
9740                          }
9741                     }
9742                }
9743           }
9744      }
9745    ITEM_WALK_END();
9746
9747    /* normal text and lines */
9748    ITEM_WALK()
9749      {
9750         Evas_Object_Textblock_Text_Item *ti;
9751         ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9752         /* NORMAL TEXT */
9753         if (ti)
9754           {
9755              COLOR_SET(normal);
9756              DRAW_TEXT(0, 0);
9757           }
9758
9759         /* STRIKETHROUGH */
9760         DRAW_FORMAT(strikethrough, (ln->h / 2), 1);
9761
9762         /* UNDERLINE */
9763         DRAW_FORMAT(underline, ln->baseline + 1, 1);
9764
9765         /* UNDERLINE DASHED */
9766         DRAW_FORMAT_DASHED(underline_dash, ln->baseline + 1, 1,
9767                          ti->parent.format->underline_dash_width,
9768                          ti->parent.format->underline_dash_gap);
9769
9770         /* UNDERLINE2 */
9771         DRAW_FORMAT(underline2, ln->baseline + 3, 1);
9772      }
9773    ITEM_WALK_END();
9774 }
9775
9776 static void
9777 evas_object_textblock_render_pre(Evas_Object *obj)
9778 {
9779    Evas_Object_Textblock *o;
9780    int is_v, was_v;
9781
9782    /* dont pre-render the obj twice! */
9783    if (obj->pre_render_done) return;
9784    obj->pre_render_done = 1;
9785    /* pre-render phase. this does anything an object needs to do just before */
9786    /* rendering. this could mean loading the image data, retrieving it from */
9787    /* elsewhere, decoding video etc. */
9788    /* then when this is done the object needs to figure if it changed and */
9789    /* if so what and where and add the appropriate redraw textblocks */
9790    o = (Evas_Object_Textblock *)(obj->object_data);
9791    if ((o->changed) || (o->content_changed) || (o->format_changed) ||
9792        ((obj->cur.geometry.w != o->last_w) ||
9793            (((o->valign != 0.0) || (o->have_ellipsis)) &&
9794                (obj->cur.geometry.h != o->last_h))))
9795      {
9796         _relayout(obj);
9797         o->redraw = 0;
9798         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9799         is_v = evas_object_is_visible(obj);
9800         was_v = evas_object_was_visible(obj);
9801         goto done;
9802      }
9803    if (o->redraw)
9804      {
9805         o->redraw = 0;
9806         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9807         is_v = evas_object_is_visible(obj);
9808         was_v = evas_object_was_visible(obj);
9809         goto done;
9810      }
9811    /* if someone is clipping this obj - go calculate the clipper */
9812    if (obj->cur.clipper)
9813      {
9814         if (obj->cur.cache.clip.dirty)
9815           evas_object_clip_recalc(obj->cur.clipper);
9816         obj->cur.clipper->func->render_pre(obj->cur.clipper);
9817      }
9818    /* now figure what changed and add draw rects */
9819    /* if it just became visible or invisible */
9820    is_v = evas_object_is_visible(obj);
9821    was_v = evas_object_was_visible(obj);
9822    if (is_v != was_v)
9823      {
9824         evas_object_render_pre_visible_change(&obj->layer->evas->clip_changes, obj, is_v, was_v);
9825         goto done;
9826      }
9827    if (obj->changed_map)
9828      {
9829         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes,
9830                                             obj);
9831         goto done;
9832      }
9833    /* it's not visible - we accounted for it appearing or not so just abort */
9834    if (!is_v) goto done;
9835    /* clipper changed this is in addition to anything else for obj */
9836    evas_object_render_pre_clipper_change(&obj->layer->evas->clip_changes, obj);
9837    /* if we restacked (layer or just within a layer) and don't clip anyone */
9838    if (obj->restack)
9839      {
9840         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9841         goto done;
9842      }
9843    /* if it changed color */
9844    if ((obj->cur.color.r != obj->prev.color.r) ||
9845        (obj->cur.color.g != obj->prev.color.g) ||
9846        (obj->cur.color.b != obj->prev.color.b) ||
9847        (obj->cur.color.a != obj->prev.color.a))
9848      {
9849         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9850         goto done;
9851      }
9852    /* if it changed geometry - and obviously not visibility or color */
9853    /* calculate differences since we have a constant color fill */
9854    /* we really only need to update the differences */
9855    if ((obj->cur.geometry.x != obj->prev.geometry.x) ||
9856        (obj->cur.geometry.y != obj->prev.geometry.y) ||
9857        (obj->cur.geometry.w != obj->prev.geometry.w) ||
9858        (obj->cur.geometry.h != obj->prev.geometry.h))
9859      {
9860         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9861         goto done;
9862      }
9863    done:
9864    evas_object_render_pre_effect_updates(&obj->layer->evas->clip_changes, obj, is_v, was_v);
9865 }
9866
9867 static void
9868 evas_object_textblock_render_post(Evas_Object *obj)
9869 {
9870 /*   Evas_Object_Textblock *o; */
9871
9872    /* this moves the current data to the previous state parts of the object */
9873    /* in whatever way is safest for the object. also if we don't need object */
9874    /* data anymore we can free it if the object deems this is a good idea */
9875 /*   o = (Evas_Object_Textblock *)(obj->object_data); */
9876    /* remove those pesky changes */
9877    evas_object_clip_changes_clean(obj);
9878    /* move cur to prev safely for object data */
9879    evas_object_cur_prev(obj);
9880 /*   o->prev = o->cur; */
9881 /*   o->changed = 0; */
9882 }
9883
9884 static unsigned int evas_object_textblock_id_get(Evas_Object *obj)
9885 {
9886    Evas_Object_Textblock *o;
9887
9888    o = (Evas_Object_Textblock *)(obj->object_data);
9889    if (!o) return 0;
9890    return MAGIC_OBJ_TEXTBLOCK;
9891 }
9892
9893 static unsigned int evas_object_textblock_visual_id_get(Evas_Object *obj)
9894 {
9895    Evas_Object_Textblock *o;
9896
9897    o = (Evas_Object_Textblock *)(obj->object_data);
9898    if (!o) return 0;
9899    return MAGIC_OBJ_CUSTOM;
9900 }
9901
9902 static void *evas_object_textblock_engine_data_get(Evas_Object *obj)
9903 {
9904    Evas_Object_Textblock *o;
9905
9906    o = (Evas_Object_Textblock *)(obj->object_data);
9907    if (!o) return NULL;
9908    return o->engine_data;
9909 }
9910
9911 static int
9912 evas_object_textblock_is_opaque(Evas_Object *obj __UNUSED__)
9913 {
9914    /* this returns 1 if the internal object data implies that the object is */
9915    /* currently fulyl opque over the entire gradient it occupies */
9916    return 0;
9917 }
9918
9919 static int
9920 evas_object_textblock_was_opaque(Evas_Object *obj __UNUSED__)
9921 {
9922    /* this returns 1 if the internal object data implies that the object was */
9923    /* currently fulyl opque over the entire gradient it occupies */
9924    return 0;
9925 }
9926
9927 static void
9928 evas_object_textblock_coords_recalc(Evas_Object *obj)
9929 {
9930    Evas_Object_Textblock *o;
9931
9932    o = (Evas_Object_Textblock *)(obj->object_data);
9933    if ((obj->cur.geometry.w != o->last_w) ||
9934        (((o->valign != 0.0) || (o->have_ellipsis)) &&
9935            (obj->cur.geometry.h != o->last_h)))
9936      {
9937         o->formatted.valid = 0;
9938         o->changed = 1;
9939      }
9940 }
9941
9942 static void
9943 evas_object_textblock_scale_update(Evas_Object *obj)
9944 {
9945    Evas_Object_Textblock *o;
9946
9947    o = (Evas_Object_Textblock *)(obj->object_data);
9948    _evas_textblock_invalidate_all(o);
9949    _evas_textblock_changed(o, obj);
9950 }
9951
9952 void
9953 _evas_object_textblock_rehint(Evas_Object *obj)
9954 {
9955    Evas_Object_Textblock *o;
9956    Evas_Object_Textblock_Paragraph *par;
9957    Evas_Object_Textblock_Line *ln;
9958
9959    o = (Evas_Object_Textblock *)(obj->object_data);
9960    EINA_INLIST_FOREACH(o->paragraphs, par)
9961      {
9962         EINA_INLIST_FOREACH(par->lines, ln)
9963           {
9964              Evas_Object_Textblock_Item *it;
9965
9966              EINA_INLIST_FOREACH(ln->items, it)
9967                {
9968                   if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
9969                     {
9970                        Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
9971                        if (ti->parent.format->font.font)
9972                          {  
9973                             evas_font_load_hinting_set(obj->layer->evas,
9974                                   ti->parent.format->font.font,
9975                                   obj->layer->evas->hinting);
9976                          }
9977                     }
9978                }
9979           }
9980      }
9981    _evas_textblock_invalidate_all(o);
9982    _evas_textblock_changed(o, obj);
9983 }
9984
9985 /**
9986  * @}
9987  */
9988
9989 #ifdef HAVE_TESTS
9990 /* return EINA_FALSE on error, used in unit_testing */
9991 EAPI Eina_Bool
9992 _evas_textblock_check_item_node_link(Evas_Object *obj)
9993 {
9994    Evas_Object_Textblock *o;
9995    Evas_Object_Textblock_Paragraph *par;
9996    Evas_Object_Textblock_Line *ln;
9997    Evas_Object_Textblock_Item *it;
9998
9999    o = (Evas_Object_Textblock *)(obj->object_data);
10000    if (!o) return EINA_FALSE;
10001
10002    if (!o->formatted.valid) _relayout(obj);
10003
10004    EINA_INLIST_FOREACH(o->paragraphs, par)
10005      {
10006         EINA_INLIST_FOREACH(par->lines, ln)
10007           {
10008              EINA_INLIST_FOREACH(ln->items, it)
10009                {
10010                   if (it->text_node != par->text_node)
10011                      return EINA_FALSE;
10012                }
10013           }
10014      }
10015    return EINA_TRUE;
10016 }
10017
10018 EAPI int
10019 _evas_textblock_format_offset_get(const Evas_Object_Textblock_Node_Format *n)
10020 {
10021    return n->offset;
10022 }
10023 #endif
10024
10025 #if 0
10026 /* Good for debugging */
10027 void
10028 pfnode(Evas_Object_Textblock_Node_Format *n)
10029 {
10030    printf("Format Node: %p\n", n);
10031    printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
10032    printf("text_node = %p, offset = %u, visible = %d\n", n->text_node, n->offset, n->visible);
10033    printf("'%s'\n", eina_strbuf_string_get(n->format));
10034 }
10035
10036 void
10037 ptnode(Evas_Object_Textblock_Node_Text *n)
10038 {
10039    printf("Text Node: %p\n", n);
10040    printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
10041    printf("format_node = %p\n", n->format_node);
10042    printf("'%ls'\n", eina_ustrbuf_string_get(n->unicode));
10043 }
10044
10045 void
10046 pitem(Evas_Object_Textblock_Item *it)
10047 {
10048    Evas_Object_Textblock_Text_Item *ti;
10049    Evas_Object_Textblock_Format_Item *fi;
10050    printf("Item: %p\n", it);
10051    printf("Type: %s (%d)\n", (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
10052          "TEXT" : "FORMAT", it->type);
10053    printf("Text pos: %d Visual pos: %d\n", it->text_pos,
10054 #ifdef BIDI_SUPPORT
10055          it->visual_pos
10056 #else
10057          it->text_pos
10058 #endif
10059          );
10060    printf("Coords: x = %d w = %d adv = %d\n", (int) it->x, (int) it->w,
10061          (int) it->adv);
10062    if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
10063      {
10064         ti = _ITEM_TEXT(it);
10065         printf("Text: '%*ls'\n", ti->text_props.text_len, GET_ITEM_TEXT(ti));
10066      }
10067    else
10068      {
10069         fi = _ITEM_FORMAT(it);
10070         printf("Format: '%s'\n", fi->item);
10071      }
10072 }
10073
10074 void
10075 ppar(Evas_Object_Textblock_Paragraph *par)
10076 {
10077    Evas_Object_Textblock_Item *it;
10078    Eina_List *i;
10079    EINA_LIST_FOREACH(par->logical_items, i, it)
10080      {
10081         printf("***********************\n");
10082         pitem(it);
10083      }
10084 }
10085
10086 #endif
10087