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