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