Revert "Rollback to previous package. evas_1.0.0.001+svn.62695slp2+build31"
[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                           is_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                           is_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->is_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
3533    if (c->par->text_node)
3534      {
3535         /* Skip this paragraph if width is the same, there is no ellipsis
3536          * and we aren't just calculating. */
3537         if (!c->par->text_node->is_new && !c->par->text_node->dirty &&
3538               !c->width_changed && c->par->lines &&
3539               !c->o->have_ellipsis)
3540           {
3541              Evas_Object_Textblock_Line *ln;
3542              /* Update c->line_no */
3543              ln = (Evas_Object_Textblock_Line *)
3544                 EINA_INLIST_GET(c->par->lines)->last;
3545              if (ln)
3546                 c->line_no = c->par->line_no + ln->line_no + 1;
3547              return 0;
3548           }
3549         c->par->text_node->dirty = EINA_FALSE;
3550         c->par->text_node->is_new = EINA_FALSE;
3551         c->par->rendered = EINA_FALSE;
3552
3553         /* Merge back and clear the paragraph */
3554           {
3555              Eina_List *itr, *itr_next;
3556              Evas_Object_Textblock_Item *ititr, *prev_it = NULL;
3557              _paragraph_clear(c->obj, c->par);
3558              EINA_LIST_FOREACH_SAFE(c->par->logical_items, itr, itr_next, ititr)
3559                {
3560                   if (ititr->merge && prev_it &&
3561                         (prev_it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
3562                         (ititr->type == EVAS_TEXTBLOCK_ITEM_TEXT))
3563                     {
3564                        _layout_item_merge_and_free(c, _ITEM_TEXT(prev_it),
3565                              _ITEM_TEXT(ititr));
3566                        c->par->logical_items =
3567                           eina_list_remove_list(c->par->logical_items, itr);
3568                     }
3569                   else
3570                     {
3571                        prev_it = ititr;
3572                     }
3573                }
3574           }
3575      }
3576
3577    c->y = c->par->y;
3578
3579    it = _ITEM(eina_list_data_get(c->par->logical_items));
3580    _layout_line_new(c, it->format);
3581    /* We walk on our own because we want to be able to add items from
3582     * inside the list and then walk them on the next iteration. */
3583    for (i = c->par->logical_items ; i ; )
3584      {
3585         int adv_line = 0;
3586         int redo_item = 0;
3587         it = _ITEM(eina_list_data_get(i));
3588         /* Skip visually deleted items */
3589         if (it->visually_deleted)
3590           {
3591              i = eina_list_next(i);
3592              continue;
3593           }
3594
3595         if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3596           {
3597              Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
3598              _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
3599                    &c->maxdescent, ti->parent.format);
3600           }
3601         else
3602           {
3603              Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
3604              if (fi->formatme)
3605                {
3606                   /* If there are no text items yet, calc ascent/descent
3607                    * according to the current format. */
3608                   if (c->maxascent + c->maxdescent == 0)
3609                      _layout_format_ascent_descent_adjust(c->obj, &c->maxascent,
3610                            &c->maxdescent, it->format);
3611
3612                   _layout_calculate_format_item_size(c->obj, fi, &c->maxascent,
3613                         &c->maxdescent, &fi->y, &fi->parent.w, &fi->parent.h);
3614                   fi->parent.adv = fi->parent.w;
3615                }
3616           }
3617
3618
3619         /* Check if we need to wrap, i.e the text is bigger than the width,
3620            or we already found a wrap point. */
3621         if ((c->w >= 0) &&
3622               (((c->x + it->adv) >
3623                 (c->w - c->o->style_pad.l - c->o->style_pad.r -
3624                  c->marginl - c->marginr)) || (wrap > 0)))
3625           {
3626              /* Handle ellipsis here. If we don't have more width left
3627               * and no height left, or no more width left and no wrapping. */
3628              if ((it->format->ellipsis == 1.0) && (c->h >= 0) &&
3629                    ((2 * it->h + c->y >
3630                      c->h - c->o->style_pad.t - c->o->style_pad.b) ||
3631                     (!it->format->wrap_word && !it->format->wrap_char &&
3632                      !it->format->wrap_mixed)))
3633                {
3634                   _layout_handle_ellipsis(c, it, i);
3635                   ret = 1;
3636                   goto end;
3637                }
3638              /* If we want to wrap and it's worth checking for wrapping
3639               * (i.e there's actually text). */
3640              else if ((it->format->wrap_word || it->format->wrap_char ||
3641                 it->format->wrap_mixed) && it->text_node)
3642                {
3643                   if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3644                     {
3645                        /* Don't wrap if it's the only item */
3646                        if (c->ln->items)
3647                          {
3648                             /*FIXME: I should handle format correctly,
3649                               i.e verify we are allowed to break here */
3650                             _layout_line_advance(c, it->format);
3651                             wrap = -1;
3652                          }
3653                     }
3654                   else
3655                     {
3656                        Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
3657                        size_t line_start;
3658
3659 #ifdef HAVE_LINEBREAK
3660                        /* If we haven't calculated the linebreaks yet,
3661                         * do */
3662                        if (!line_breaks)
3663                          {
3664                             /* Only relevant in those cases */
3665                             if (it->format->wrap_word || it->format->wrap_mixed)
3666                               {
3667                                  const char *lang;
3668                                  lang = (it->format->font.fdesc) ?
3669                                     it->format->font.fdesc->lang : "";
3670                                  size_t len =
3671                                     eina_ustrbuf_length_get(
3672                                           it->text_node->unicode);
3673                                  line_breaks = malloc(len);
3674                                  set_linebreaks_utf32((const utf32_t *)
3675                                     eina_ustrbuf_string_get(
3676                                        it->text_node->unicode),
3677                                     len, lang, line_breaks);
3678                               }
3679                          }
3680 #endif
3681                        if (c->ln->items)
3682                           line_start = c->ln->items->text_pos;
3683                        else
3684                           line_start = ti->parent.text_pos;
3685
3686                        adv_line = 1;
3687                        /* If we don't already have a wrap point from before */
3688                        if (wrap < 0)
3689                          {
3690                             if (it->format->wrap_word)
3691                                wrap = _layout_get_wordwrap(c, it->format, ti,
3692                                      line_start, line_breaks);
3693                             else if (it->format->wrap_char)
3694                                wrap = _layout_get_charwrap(c, it->format, ti,
3695                                      line_start, line_breaks);
3696                             else if (it->format->wrap_mixed)
3697                                wrap = _layout_get_mixedwrap(c, it->format, ti,
3698                                      line_start, line_breaks);
3699                             else
3700                                wrap = -1;
3701                          }
3702
3703                        /* If it's before the item, rollback and apply.
3704                           if it's in the item, cut.
3705                           If it's after the item, delay the cut */
3706                        if (wrap > 0)
3707                          {
3708                             size_t uwrap = (size_t) wrap;
3709                             if (uwrap < ti->parent.text_pos)
3710                               {
3711                                  /* Rollback latest additions, and cut that
3712                                     item */
3713                                  i = eina_list_prev(i);
3714                                  it = eina_list_data_get(i);
3715                                  while (uwrap < it->text_pos)
3716                                    {
3717                                       c->ln->items = _ITEM(
3718                                             eina_inlist_remove(
3719                                                EINA_INLIST_GET(c->ln->items),
3720                                                EINA_INLIST_GET(it)));
3721                                       i = eina_list_prev(i);
3722                                       it = eina_list_data_get(i);
3723                                    }
3724                                  c->x = it->x;
3725                                  c->ln->items = _ITEM(
3726                                        eina_inlist_remove(
3727                                           EINA_INLIST_GET(c->ln->items),
3728                                           EINA_INLIST_GET(it)));
3729                                  continue;
3730                               }
3731                             /* If it points to the end, it means the previous
3732                              * char is a whitespace we should remove, so this
3733                              * is a wanted cutting point. */
3734                             else if (uwrap > ti->parent.text_pos +
3735                                   ti->text_props.text_len)
3736                                wrap = -1; /* Delay the cut in a smart way
3737                                i.e use the item_pos as the line_start, because
3738                                there's already no cut before*/
3739                             else
3740                                wrap -= ti->parent.text_pos; /* Cut here */
3741                          }
3742
3743                        if (wrap > 0)
3744                          {
3745                             _layout_item_text_split_strip_white(c, ti, i, wrap);
3746                          }
3747                        else if (wrap == 0)
3748                          {
3749                             /* Should wrap before the item */
3750                             adv_line = 0;
3751                             redo_item = 1;
3752                             _layout_line_advance(c, it->format);
3753                          }
3754                        /* Reset wrap */
3755                        wrap = -1;
3756                     }
3757                }
3758           }
3759
3760         if (!redo_item && !it->visually_deleted)
3761           {
3762              c->ln->items = (Evas_Object_Textblock_Item *)
3763                 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
3764                       EINA_INLIST_GET(it));
3765              if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3766                {
3767                   Evas_Object_Textblock_Format_Item *fi;
3768                   fi = _ITEM_FORMAT(it);
3769                   fi->y = c->y;
3770                   /* If it's a newline, and we are not in newline compat
3771                    * mode, or we are in newline compat mode, and this is
3772                    * not used as a paragraph separator, advance */
3773                   if (fi->item && _IS_LINE_SEPARATOR(fi->item) &&
3774                         (!c->o->legacy_newline ||
3775                          eina_list_next(i)))
3776                     {
3777                        adv_line = 1;
3778                     }
3779                }
3780              c->x += it->adv;
3781              i = eina_list_next(i);
3782           }
3783         if (adv_line)
3784           {
3785              /* Each line is according to the first item in it, and here
3786               * i is already the next item (or the current if we redo it) */
3787              if (i)
3788                {
3789                   it = _ITEM(eina_list_data_get(i));
3790                }
3791              _layout_line_advance(c, it->format);
3792           }
3793      }
3794    if (c->ln->items)
3795      {
3796         /* Here 'it' is the last format used */
3797         _layout_line_finalize(c, it->format);
3798      }
3799
3800 end:
3801 #ifdef HAVE_LINEBREAK
3802    if (line_breaks)
3803       free(line_breaks);
3804 #endif
3805
3806    return ret;
3807 }
3808
3809 /**
3810  * @internal
3811  * Invalidate text nodes according to format changes
3812  * This goes through all the new format changes and marks the text nodes
3813  * that should be invalidated because of format changes.
3814  *
3815  * @param c the working context.
3816  */
3817 static inline void
3818 _format_changes_invalidate_text_nodes(Ctxt *c)
3819 {
3820    Evas_Object_Textblock_Node_Format *fnode = c->o->format_nodes;
3821    Evas_Object_Textblock_Node_Text *start_n = NULL;
3822    Eina_List *fstack = NULL;
3823    int balance = 0;
3824    while (fnode)
3825      {
3826         if (fnode->is_new)
3827           {
3828              const char *fstr = fnode->orig_format;
3829              /* balance < 0 means we gave up and everything should be
3830               * invalidated */
3831              if (*fstr == '+')
3832                {
3833                   balance++;
3834                   if (!fstack)
3835                      start_n = fnode->text_node;
3836                   fstack = eina_list_prepend(fstack, fnode);
3837                }
3838              else if (*fstr == '-')
3839                {
3840                   size_t fstr_len;
3841                   /* Skip the '-' */
3842                   fstr++;
3843                   fstr_len = strlen(fstr);
3844                   /* Generic popper, just pop */
3845                   if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
3846                     {
3847                        fstack = eina_list_remove_list(fstack, fstack);
3848                        balance--;
3849                     }
3850                   /* Find the matching format and pop it, if the matching format
3851                    * is out format, i.e the last one, pop and break. */
3852                   else
3853                     {
3854                        Eina_List *i;
3855                        Evas_Object_Textblock_Node_Format *fnode2;
3856                        EINA_LIST_FOREACH(fstack, i, fnode2)
3857                          {
3858                             if (_FORMAT_IS_CLOSER_OF(
3859                                      fnode2->orig_format, fstr, fstr_len))
3860                               {
3861                                  fstack = eina_list_remove_list(fstack, i);
3862                                  break;
3863                               }
3864                          }
3865                        balance--;
3866                     }
3867
3868                   if (!fstack)
3869                     {
3870                        Evas_Object_Textblock_Node_Text *f_tnode =
3871                           fnode->text_node;
3872                        while (start_n)
3873                          {
3874                             start_n->dirty = EINA_TRUE;
3875                             if (start_n == f_tnode)
3876                                break;
3877                             start_n =
3878                                _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
3879                          }
3880                        start_n = NULL;
3881                     }
3882                }
3883              else if (!fnode->visible)
3884                 balance = -1;
3885
3886              if (balance < 0)
3887                {
3888                   /* if we don't already have a starting point, use the
3889                    * current paragraph. */
3890                   if (!start_n)
3891                      start_n = fnode->text_node;
3892                   break;
3893                }
3894           }
3895         fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
3896      }
3897
3898    if (balance != 0)
3899      {
3900         while (start_n)
3901           {
3902              start_n->dirty = EINA_TRUE;
3903              start_n = _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
3904           }
3905      }
3906 }
3907
3908
3909 /** FIXME: Document */
3910 static void
3911 _layout_pre(Ctxt *c, int *style_pad_l, int *style_pad_r, int *style_pad_t,
3912       int *style_pad_b)
3913 {
3914    Evas_Object *obj = c->obj;
3915    Evas_Object_Textblock *o = c->o;
3916    /* Mark text nodes as dirty if format have changed. */
3917    if (c->o->format_changed)
3918      {
3919         _format_changes_invalidate_text_nodes(c);
3920      }
3921
3922    if (o->content_changed)
3923      {
3924         Evas_Object_Textblock_Node_Text *n;
3925         c->o->have_ellipsis = 0;
3926         c->par = c->paragraphs = o->paragraphs;
3927         /* Go through all the text nodes to create the logical layout */
3928         EINA_INLIST_FOREACH(c->o->text_nodes, n)
3929           {
3930              Evas_Object_Textblock_Node_Format *fnode;
3931              size_t start;
3932              int off;
3933
3934              /* If it's not a new paragraph, either update it or skip it.
3935               * Remove all the paragraphs that were deleted */
3936              if (!n->is_new)
3937                {
3938                   /* Remove all the deleted paragraphs at this point */
3939                   while (c->par->text_node != n)
3940                     {
3941                        Evas_Object_Textblock_Paragraph *tmp_par =
3942                           (Evas_Object_Textblock_Paragraph *)
3943                           EINA_INLIST_GET(c->par)->next;
3944
3945                        c->paragraphs = (Evas_Object_Textblock_Paragraph *)
3946                           eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
3947                                 EINA_INLIST_GET(c->par));
3948                        _paragraph_free(obj, c->par);
3949
3950                        c->par = tmp_par;
3951                     }
3952
3953                   /* If it's dirty, remove and recreate, if it's clean,
3954                    * skip to the next. */
3955                   if (n->dirty)
3956                     {
3957                        Evas_Object_Textblock_Paragraph *prev_par = c->par;
3958
3959                        _layout_paragraph_new(c, n, EINA_TRUE);
3960
3961                        c->paragraphs = (Evas_Object_Textblock_Paragraph *)
3962                           eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
3963                                 EINA_INLIST_GET(prev_par));
3964                        _paragraph_free(obj, prev_par);
3965                     }
3966                   else
3967                     {
3968                        c->par = (Evas_Object_Textblock_Paragraph *)
3969                           EINA_INLIST_GET(c->par)->next;
3970
3971                        /* Update the format stack according to the node's
3972                         * formats */
3973                        fnode = n->format_node;
3974                        while (fnode && (fnode->text_node == n))
3975                          {
3976                             /* Only do this if this actually changes format */
3977                             if (fnode->format_change)
3978                                _layout_do_format(obj, c, &c->fmt, fnode,
3979                                      style_pad_l, style_pad_r,
3980                                      style_pad_t, style_pad_b, EINA_FALSE);
3981                             fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
3982                          }
3983                        continue;
3984                     }
3985                }
3986              else
3987                {
3988                   /* If it's a new paragraph, just add it. */
3989                   _layout_paragraph_new(c, n, EINA_FALSE);
3990                }
3991
3992 #ifdef BIDI_SUPPORT
3993              _layout_update_bidi_props(c->o, c->par);
3994 #endif
3995
3996              /* For each text node to thorugh all of it's format nodes
3997               * append text from the start to the offset of the next format
3998               * using the last format got. if needed it also creates format
3999               * items this is the core algorithm of the layout mechanism.
4000               * Skip the unicode replacement chars when there are because
4001               * we don't want to print them. */
4002              fnode = n->format_node;
4003              start = off = 0;
4004              while (fnode && (fnode->text_node == n))
4005                {
4006                   off += fnode->offset;
4007                   /* No need to skip on the first run, or a non-visible one */
4008                   _layout_text_append(c, c->fmt, n, start, off, o->repch);
4009                   _layout_do_format(obj, c, &c->fmt, fnode, style_pad_l,
4010                         style_pad_r, style_pad_t, style_pad_b, EINA_TRUE);
4011                   if ((c->have_underline2) || (c->have_underline))
4012                     {
4013                        if (*style_pad_b < c->underline_extend)
4014                          *style_pad_b = c->underline_extend;
4015                        c->have_underline = 0;
4016                        c->have_underline2 = 0;
4017                        c->underline_extend = 0;
4018                     }
4019                   start += off;
4020                   if (fnode->visible)
4021                     {
4022                        off = -1;
4023                        start++;
4024                     }
4025                   else
4026                     {
4027                        off = 0;
4028                     }
4029                   fnode->is_new = EINA_FALSE;
4030                   fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
4031                }
4032              _layout_text_append(c, c->fmt, n, start, -1, o->repch);
4033 #ifdef BIDI_SUPPORT
4034              /* Clear the bidi props because we don't need them anymore. */
4035              if (c->par->bidi_props)
4036                {
4037                   evas_bidi_paragraph_props_unref(c->par->bidi_props);
4038                   c->par->bidi_props = NULL;
4039                }
4040 #endif
4041              c->par = (Evas_Object_Textblock_Paragraph *)
4042                 EINA_INLIST_GET(c->par)->next;
4043           }
4044
4045         /* Delete the rest of the layout paragraphs */
4046         while (c->par)
4047           {
4048              Evas_Object_Textblock_Paragraph *tmp_par =
4049                 (Evas_Object_Textblock_Paragraph *)
4050                 EINA_INLIST_GET(c->par)->next;
4051
4052              c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4053                 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
4054                       EINA_INLIST_GET(c->par));
4055              _paragraph_free(obj, c->par);
4056
4057              c->par = tmp_par;
4058           }
4059         o->paragraphs = c->paragraphs;
4060         c->par = NULL;
4061      }
4062
4063 }
4064
4065 /**
4066  * @internal
4067  * Create the layout from the nodes.
4068  *
4069  * @param obj the evas object - NOT NULL.
4070  * @param calc_only true if should only calc sizes false if should also create the layout.. It assumes native size is being calculated, doesn't support formatted size atm.
4071  * @param w the object's w, -1 means no wrapping (i.e infinite size)
4072  * @param h the object's h, -1 means inifinte size.
4073  * @param w_ret the object's calculated w.
4074  * @param h_ret the object's calculated h.
4075  */
4076 static void
4077 _layout(const Evas_Object *obj, int w, int h, int *w_ret, int *h_ret)
4078 {
4079    Evas_Object_Textblock *o;
4080    Ctxt ctxt, *c;
4081    int style_pad_l = 0, style_pad_r = 0, style_pad_t = 0, style_pad_b = 0;
4082
4083    /* setup context */
4084    o = (Evas_Object_Textblock *)(obj->object_data);
4085    c = &ctxt;
4086    c->obj = (Evas_Object *)obj;
4087    c->o = o;
4088    c->paragraphs = c->par = NULL;
4089    c->format_stack = NULL;
4090    c->fmt = NULL;
4091    c->x = c->y = 0;
4092    c->w = w;
4093    c->h = h;
4094    c->wmax = c->hmax = 0;
4095    c->maxascent = c->maxdescent = 0;
4096    c->marginl = c->marginr = 0;
4097    c->have_underline = 0;
4098    c->have_underline2 = 0;
4099    c->underline_extend = 0;
4100    c->line_no = 0;
4101    c->align = 0.0;
4102    c->align_auto = EINA_TRUE;
4103    c->ln = NULL;
4104    c->width_changed = (obj->cur.geometry.w != o->last_w);
4105
4106    /* Start of logical layout creation */
4107    /* setup default base style */
4108    if ((c->o->style) && (c->o->style->default_tag))
4109      {
4110         c->fmt = _layout_format_push(c, NULL, NULL);
4111         _format_fill(c->obj, c->fmt, c->o->style->default_tag);
4112         _format_finalize(c->obj, c->fmt);
4113      }
4114    if (!c->fmt)
4115      {
4116         if (w_ret) *w_ret = 0;
4117         if (h_ret) *h_ret = 0;
4118         return;
4119      }
4120
4121    _layout_pre(c, &style_pad_l, &style_pad_r, &style_pad_t, &style_pad_b);
4122    c->paragraphs = o->paragraphs;
4123
4124    /* If there are no paragraphs, create the minimum needed,
4125     * if the last paragraph has no lines/text, create that as well */
4126    if (!c->paragraphs)
4127      {
4128         _layout_paragraph_new(c, NULL, EINA_TRUE);
4129         o->paragraphs = c->paragraphs;
4130      }
4131    c->par = (Evas_Object_Textblock_Paragraph *)
4132       EINA_INLIST_GET(c->paragraphs)->last;
4133    if (!c->par->logical_items)
4134      {
4135         Evas_Object_Textblock_Text_Item *ti;
4136         ti = _layout_text_item_new(c, c->fmt);
4137         ti->parent.text_node = c->par->text_node;
4138         ti->parent.text_pos = 0;
4139         _layout_text_add_logical_item(c, ti, NULL);
4140      }
4141
4142    /* End of logical layout creation */
4143
4144    /* Start of visual layout creation */
4145    {
4146       Evas_Object_Textblock_Paragraph *last_vis_par = NULL;
4147       int par_index_step = o->num_paragraphs / TEXTBLOCK_PAR_INDEX_SIZE;
4148       int par_count = 1; /* Force it to take the first one */
4149       int par_index_pos = 0;
4150
4151       if (par_index_step == 0) par_index_step = 1;
4152
4153       /* Clear all of the index */
4154       memset(o->par_index, 0, sizeof(o->par_index));
4155
4156       EINA_INLIST_FOREACH(c->paragraphs, c->par)
4157         {
4158            _layout_update_par(c);
4159
4160            /* Break if we should stop here. */
4161            if (_layout_par(c))
4162              {
4163                 last_vis_par = c->par;
4164                 break;
4165              }
4166
4167            if ((par_index_pos < TEXTBLOCK_PAR_INDEX_SIZE) && (--par_count == 0))
4168              {
4169                 par_count = par_index_step;
4170
4171                 o->par_index[par_index_pos++] = c->par;
4172              }
4173         }
4174
4175       /* Mark all the rest of the paragraphs as invisible */
4176       if (c->par)
4177         {
4178            c->par = (Evas_Object_Textblock_Paragraph *)
4179               EINA_INLIST_GET(c->par)->next;
4180            while (c->par)
4181              {
4182                 c->par->visible = 0;
4183                 c->par = (Evas_Object_Textblock_Paragraph *)
4184                    EINA_INLIST_GET(c->par)->next;
4185              }
4186         }
4187
4188       /* Get the last visible paragraph in the layout */
4189       if (!last_vis_par && c->paragraphs)
4190          last_vis_par = (Evas_Object_Textblock_Paragraph *)
4191             EINA_INLIST_GET(c->paragraphs)->last;
4192
4193       if (last_vis_par)
4194          c->hmax = last_vis_par->y + last_vis_par->h;
4195    }
4196
4197    /* Clean the rest of the format stack */
4198    while (c->format_stack)
4199      {
4200         c->fmt = c->format_stack->data;
4201         c->format_stack = eina_list_remove_list(c->format_stack, c->format_stack);
4202         _format_unref_free(c->obj, c->fmt);
4203      }
4204
4205    if (w_ret) *w_ret = c->wmax;
4206    if (h_ret) *h_ret = c->hmax;
4207
4208    /* Vertically align the textblock */
4209    if ((o->valign > 0.0) && (c->h > c->hmax))
4210      {
4211         Evas_Coord adjustment = (c->h - c->hmax) * o->valign;
4212         Evas_Object_Textblock_Paragraph *par;
4213         EINA_INLIST_FOREACH(c->paragraphs, par)
4214           {
4215              par->y += adjustment;
4216           }
4217      }
4218
4219    if ((o->style_pad.l != style_pad_l) || (o->style_pad.r != style_pad_r) ||
4220        (o->style_pad.t != style_pad_t) || (o->style_pad.b != style_pad_b))
4221      {
4222         o->style_pad.l = style_pad_l;
4223         o->style_pad.r = style_pad_r;
4224         o->style_pad.t = style_pad_t;
4225         o->style_pad.b = style_pad_b;
4226         _paragraphs_clear(obj, c->paragraphs);
4227         _layout(obj, w, h, w_ret, h_ret);
4228      }
4229 }
4230
4231 /*
4232  * @internal
4233  * Relayout the object according to current object size.
4234  *
4235  * @param obj the evas object - NOT NULL.
4236  */
4237 static void
4238 _relayout(const Evas_Object *obj)
4239 {
4240    Evas_Object_Textblock *o;
4241
4242    o = (Evas_Object_Textblock *)(obj->object_data);
4243    _layout(obj, obj->cur.geometry.w, obj->cur.geometry.h,
4244          &o->formatted.w, &o->formatted.h);
4245    o->formatted.valid = 1;
4246    o->last_w = obj->cur.geometry.w;
4247    o->last_h = obj->cur.geometry.h;
4248    o->changed = 0;
4249    o->content_changed = 0;
4250    o->format_changed = EINA_FALSE;
4251    o->redraw = 1;
4252 }
4253
4254 /**
4255  * @internal
4256  * Find the layout item and line that match the text node and position passed.
4257  *
4258  * @param obj the evas object - NOT NULL.
4259  * @param n the text node - Not null.
4260  * @param pos the position to look for - valid.
4261  * @param[out] lnr the line found - not null.
4262  * @param[out] tir the item found - not null.
4263  * @see _find_layout_format_item_line_match()
4264  */
4265 static void
4266 _find_layout_item_line_match(Evas_Object *obj, Evas_Object_Textblock_Node_Text *n, int pos, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Item **itr)
4267 {
4268    Evas_Object_Textblock_Paragraph *found_par;
4269    Evas_Object_Textblock_Line *ln;
4270    Evas_Object_Textblock *o;
4271
4272    o = (Evas_Object_Textblock *)(obj->object_data);
4273    if (!o->formatted.valid) _relayout(obj);
4274
4275    found_par = n->par;
4276    if (found_par)
4277      {
4278         _layout_paragraph_render(o, found_par);
4279         EINA_INLIST_FOREACH(found_par->lines, ln)
4280           {
4281              Evas_Object_Textblock_Item *it;
4282
4283              EINA_INLIST_FOREACH(ln->items, it)
4284                {
4285                   /* FIXME: p should be size_t, same goes for pos */
4286                   int p = (int) it->text_pos;
4287
4288                   if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
4289                     {
4290                        Evas_Object_Textblock_Text_Item *ti =
4291                           _ITEM_TEXT(it);
4292
4293                        p += (int) ti->text_props.text_len;
4294                     }
4295                   else
4296                     {
4297                        p++;
4298                     }
4299
4300                   if (((pos >= (int) it->text_pos) && (pos < p)))
4301                     {
4302                        *lnr = ln;
4303                        *itr = it;
4304                        return;
4305                     }
4306                   else if (p == pos)
4307                     {
4308                        *lnr = ln;
4309                        *itr = it;
4310                     }
4311                }
4312           }
4313      }
4314 }
4315
4316 /**
4317  * @internal
4318  * Return the line number 'line'.
4319  *
4320  * @param obj the evas object - NOT NULL.
4321  * @param line the line to find
4322  * @return the line of line number or NULL if no line found.
4323  */
4324 static Evas_Object_Textblock_Line *
4325 _find_layout_line_num(const Evas_Object *obj, int line)
4326 {
4327    Evas_Object_Textblock_Paragraph *par;
4328    Evas_Object_Textblock_Line *ln;
4329    Evas_Object_Textblock *o;
4330
4331    o = (Evas_Object_Textblock *)(obj->object_data);
4332
4333    par = _layout_find_paragraph_by_line_no(o, line);
4334    if (par)
4335      {
4336         _layout_paragraph_render(o, par);
4337         EINA_INLIST_FOREACH(par->lines, ln)
4338           {
4339              if (par->line_no + ln->line_no == line) return ln;
4340           }
4341      }
4342    return NULL;
4343 }
4344
4345 EAPI Evas_Object *
4346 evas_object_textblock_add(Evas *e)
4347 {
4348    Evas_Object *obj;
4349
4350    MAGIC_CHECK(e, Evas, MAGIC_EVAS);
4351    return NULL;
4352    MAGIC_CHECK_END();
4353    obj = evas_object_new(e);
4354    evas_object_textblock_init(obj);
4355    evas_object_inject(obj, e);
4356    return obj;
4357 }
4358
4359 EAPI Evas_Textblock_Style *
4360 evas_textblock_style_new(void)
4361 {
4362    Evas_Textblock_Style *ts;
4363
4364    ts = calloc(1, sizeof(Evas_Textblock_Style));
4365    return ts;
4366 }
4367
4368 EAPI void
4369 evas_textblock_style_free(Evas_Textblock_Style *ts)
4370 {
4371    if (!ts) return;
4372    if (ts->objects)
4373      {
4374         ts->delete_me = 1;
4375         return;
4376      }
4377    _style_clear(ts);
4378    free(ts);
4379 }
4380
4381 EAPI void
4382 evas_textblock_style_set(Evas_Textblock_Style *ts, const char *text)
4383 {
4384    Eina_List *l;
4385    Evas_Object *obj;
4386
4387    if (!ts) return;
4388    /* If the style wasn't really changed, abort. */
4389    if ((!ts->style_text && !text) ||
4390        (ts->style_text && text && !strcmp(text, ts->style_text)))
4391       return;
4392
4393    EINA_LIST_FOREACH(ts->objects, l, obj)
4394      {
4395         Evas_Object_Textblock *o;
4396
4397         o = (Evas_Object_Textblock *)(obj->object_data);
4398         _evas_textblock_invalidate_all(o);
4399         _evas_textblock_changed(o, obj);
4400      }
4401
4402    _style_replace(ts, text);
4403
4404    if (ts->style_text)
4405      {
4406         // format MUST be KEY='VALUE'[KEY='VALUE']...
4407         const char *p;
4408         const char *key_start, *key_stop, *val_start, *val_stop;
4409
4410         key_start = key_stop = val_start = val_stop = NULL;
4411         p = ts->style_text;
4412         while (*p)
4413           {
4414              if (!key_start)
4415                {
4416                   if (!isspace(*p))
4417                     key_start = p;
4418                }
4419              else if (!key_stop)
4420                {
4421                   if ((*p == '=') || (isspace(*p)))
4422                     key_stop = p;
4423                }
4424              else if (!val_start)
4425                {
4426                   if (((*p) == '\'') && (*(p + 1)))
4427                     val_start = p + 1;
4428                }
4429              else if (!val_stop)
4430                {
4431                   if (((*p) == '\'') && (p > ts->style_text) && (p[-1] != '\\'))
4432                     val_stop = p;
4433                }
4434              if ((key_start) && (key_stop) && (val_start) && (val_stop))
4435                {
4436                   char *tags, *replaces;
4437                   Evas_Object_Style_Tag *tag;
4438                   size_t tag_len = key_stop - key_start;
4439                   size_t replace_len = val_stop - val_start;
4440
4441                   tags = malloc(tag_len + 1);
4442                   if (tags)
4443                     {
4444                        memcpy(tags, key_start, tag_len);
4445                        tags[tag_len] = 0;
4446                     }
4447
4448                   replaces = malloc(replace_len + 1);
4449                   if (replaces)
4450                     {
4451                        memcpy(replaces, val_start, replace_len);
4452                        replaces[replace_len] = 0;
4453                     }
4454                   if ((tags) && (replaces))
4455                     {
4456                        if (!strcmp(tags, "DEFAULT"))
4457                          {
4458                             ts->default_tag = replaces;
4459                             free(tags);
4460                          }
4461                        else
4462                          {
4463                             tag = calloc(1, sizeof(Evas_Object_Style_Tag));
4464                             if (tag)
4465                               {
4466                                  tag->tag = tags;
4467                                  tag->replace = replaces;
4468                                  tag->tag_len = tag_len;
4469                                  tag->replace_len = replace_len;
4470                                  ts->tags = (Evas_Object_Style_Tag *)eina_inlist_append(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
4471                               }
4472                             else
4473                               {
4474                                  free(tags);
4475                                  free(replaces);
4476                               }
4477                          }
4478                     }
4479                   else
4480                     {
4481                        if (tags) free(tags);
4482                        if (replaces) free(replaces);
4483                     }
4484                   key_start = key_stop = val_start = val_stop = NULL;
4485                }
4486              p++;
4487           }
4488      }
4489 }
4490
4491 EAPI const char *
4492 evas_textblock_style_get(const Evas_Textblock_Style *ts)
4493 {
4494    if (!ts) return NULL;
4495    return ts->style_text;
4496 }
4497
4498 /* textblock styles */
4499 EAPI void
4500 evas_object_textblock_style_set(Evas_Object *obj, Evas_Textblock_Style *ts)
4501 {
4502    TB_HEAD();
4503    if (ts == o->style) return;
4504    if ((ts) && (ts->delete_me)) return;
4505    if (o->style)
4506      {
4507         Evas_Textblock_Style *old_ts;
4508         if (o->markup_text)
4509           {
4510              free(o->markup_text);
4511              o->markup_text = NULL;
4512           }
4513
4514         old_ts = o->style;
4515         old_ts->objects = eina_list_remove(old_ts->objects, obj);
4516         if ((old_ts->delete_me) && (!old_ts->objects))
4517           evas_textblock_style_free(old_ts);
4518      }
4519    if (ts)
4520      {
4521         ts->objects = eina_list_append(ts->objects, obj);
4522      }
4523    o->style = ts;
4524
4525    _evas_textblock_invalidate_all(o);
4526    _evas_textblock_changed(o, obj);
4527 }
4528
4529 EAPI const Evas_Textblock_Style *
4530 evas_object_textblock_style_get(const Evas_Object *obj)
4531 {
4532    TB_HEAD_RETURN(NULL);
4533    return o->style;
4534 }
4535
4536 EAPI void
4537 evas_object_textblock_replace_char_set(Evas_Object *obj, const char *ch)
4538 {
4539    TB_HEAD();
4540    if (o->repch) eina_stringshare_del(o->repch);
4541    if (ch) o->repch = eina_stringshare_add(ch);
4542    else o->repch = NULL;
4543    _evas_textblock_invalidate_all(o);
4544    _evas_textblock_changed(o, obj);
4545 }
4546
4547 EAPI void
4548 evas_object_textblock_legacy_newline_set(Evas_Object *obj, Eina_Bool mode)
4549 {
4550    TB_HEAD();
4551    if (o->legacy_newline == mode)
4552       return;
4553
4554    o->legacy_newline = mode;
4555    /* FIXME: Should recreate all the textnodes... For now, it's just
4556     * for new text inserted. */
4557 }
4558
4559 EAPI Eina_Bool
4560 evas_object_textblock_legacy_newline_get(const Evas_Object *obj)
4561 {
4562    TB_HEAD_RETURN(EINA_FALSE);
4563    return o->legacy_newline;
4564 }
4565
4566 EAPI void
4567 evas_object_textblock_valign_set(Evas_Object *obj, double align)
4568 {
4569    TB_HEAD();
4570    if (align < 0.0) align = 0.0;
4571    else if (align > 1.0) align = 1.0;
4572    if (o->valign == align) return;
4573    o->valign = align;
4574    _evas_textblock_changed(o, obj);
4575 }
4576
4577 EAPI double
4578 evas_object_textblock_valign_get(const Evas_Object *obj)
4579 {
4580    TB_HEAD_RETURN(0.0);
4581    return o->valign;
4582 }
4583
4584 EAPI void
4585 evas_object_textblock_bidi_delimiters_set(Evas_Object *obj, const char *delim)
4586 {
4587    TB_HEAD();
4588    eina_stringshare_replace(&o->bidi_delimiters, delim);
4589 }
4590
4591 EAPI const char *
4592 evas_object_textblock_bidi_delimiters_get(const Evas_Object *obj)
4593 {
4594    TB_HEAD_RETURN(NULL);
4595    return o->bidi_delimiters;
4596 }
4597
4598 EAPI const char *
4599 evas_object_textblock_replace_char_get(Evas_Object *obj)
4600 {
4601    TB_HEAD_RETURN(NULL);
4602    return o->repch;
4603 }
4604
4605 /**
4606  * @internal
4607  * Advance p_buff to point after the end of the string. It's used with the
4608  * @ref escaped_strings[] variable.
4609  *
4610  * @param p_buff the pointer to the current string.
4611  */
4612 static inline void
4613 _escaped_advance_after_end_of_string(const char **p_buf)
4614 {
4615    while (**p_buf != 0) (*p_buf)++;
4616    (*p_buf)++;
4617 }
4618
4619 /**
4620  * @internal
4621  * Advance p_buff to point after the end of the string. It's used with the
4622  * @ref escaped_strings[] variable. Also chec if matches.
4623  * FIXME: doc.
4624  *
4625  * @param p_buff the pointer to the current string.
4626  */
4627 static inline int
4628 _escaped_is_eq_and_advance(const char *s, const char *s_end,
4629       const char **p_m, const char *m_end)
4630 {
4631    Eina_Bool reached_end;
4632    for (;((s < s_end) && (*p_m < m_end)); s++, (*p_m)++)
4633      {
4634         if (*s != **p_m)
4635           {
4636              _escaped_advance_after_end_of_string(p_m);
4637              return 0;
4638           }
4639      }
4640
4641    reached_end = !**p_m;
4642    if (*p_m < m_end)
4643      _escaped_advance_after_end_of_string(p_m);
4644
4645    return ((s == s_end) && reached_end);
4646 }
4647
4648 /**
4649  * @internal
4650  *
4651  * @param s the string to match
4652  */
4653 static inline const char *
4654 _escaped_char_match(const char *s, int *adv)
4655 {
4656    const char *map_itr, *map_end, *mc, *sc;
4657
4658    map_itr = escape_strings;
4659    map_end = map_itr + sizeof(escape_strings);
4660
4661    while (map_itr < map_end)
4662      {
4663         const char *escape;
4664         int match;
4665
4666         escape = map_itr;
4667         _escaped_advance_after_end_of_string(&map_itr);
4668         if (map_itr >= map_end) break;
4669
4670         mc = map_itr;
4671         sc = s;
4672         match = 1;
4673         while ((*mc) && (*sc))
4674           {
4675              if ((unsigned char)*sc < (unsigned char)*mc) return NULL;
4676              if (*sc != *mc) match = 0;
4677              mc++;
4678              sc++;
4679           }
4680         if (match)
4681           {
4682              *adv = mc - map_itr;
4683              return escape;
4684           }
4685         _escaped_advance_after_end_of_string(&map_itr);
4686      }
4687    return NULL;
4688 }
4689
4690 /**
4691  * @internal
4692  * FIXME: TBD.
4693  *
4694  * @param s the string to match
4695  */
4696 static inline const char *
4697 _escaped_char_get(const char *s, const char *s_end)
4698 {
4699    /* Handle numeric escape codes. */
4700    if (s[1] == '#')
4701      {
4702         static char utf8_escape[7]; /* Support up to 6 bytes utf8 */
4703         char ustr[10];
4704         Eina_Unicode uchar[2] = { 0, 0 };
4705         char *utf8_char;
4706         size_t len = 0;
4707         int base = 10;
4708         s += 2; /* Skip "&#" */
4709
4710         if (tolower(*s) == 'x')
4711           {
4712              s++;
4713              base = 16;
4714           }
4715
4716         len = s_end - s;
4717         if (len >= sizeof(ustr) + 1)
4718            len = sizeof(ustr);
4719
4720         memcpy(ustr, s, len);
4721         ustr[len] = '\0';
4722         uchar[0] = strtol(ustr, NULL, base);
4723
4724         if (uchar[0] == 0)
4725           return NULL;
4726
4727         utf8_char = eina_unicode_unicode_to_utf8(uchar, NULL);
4728         strcpy(utf8_escape, utf8_char);
4729         free(utf8_char);
4730
4731         return utf8_escape;
4732      }
4733    else
4734      {
4735         const char *map_itr, *map_end;
4736
4737         map_itr = escape_strings;
4738         map_end = map_itr + sizeof(escape_strings);
4739
4740         while (map_itr < map_end)
4741           {
4742              if (_escaped_is_eq_and_advance(s, s_end, &map_itr, map_end))
4743                 return map_itr;
4744              if (map_itr < map_end)
4745                 _escaped_advance_after_end_of_string(&map_itr);
4746           }
4747      }
4748
4749    return NULL;
4750 }
4751
4752 EAPI const char *
4753 evas_textblock_escape_string_get(const char *escape)
4754 {
4755    /* &amp; -> & */
4756    return _escaped_char_get(escape, escape + strlen(escape));
4757 }
4758
4759 EAPI const char *
4760 evas_textblock_escape_string_range_get(const char *escape_start, const char *escape_end)
4761 {
4762    return _escaped_char_get(escape_start, escape_end);
4763 }
4764
4765 EAPI const char *
4766 evas_textblock_string_escape_get(const char *string, int *len_ret)
4767 {
4768    /* & -> &amp; */
4769    return _escaped_char_match(string, len_ret);
4770 }
4771
4772 /**
4773  * @internal
4774  * Appends the escaped char beteewn s and s_end to the curosr
4775  *
4776  *
4777  * @param s the start of the string
4778  * @param s_end the end of the string.
4779  */
4780 static inline void
4781 _append_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
4782       const char *s_end)
4783 {
4784    const char *escape;
4785
4786    escape = _escaped_char_get(s, s_end);
4787    if (escape)
4788      evas_textblock_cursor_text_append(cur, escape);
4789 }
4790
4791 /**
4792  * @internal
4793  * prepends the escaped char beteewn s and s_end to the curosr
4794  *
4795  *
4796  * @param s the start of the string
4797  * @param s_end the end of the string.
4798  */
4799 static inline void
4800 _prepend_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
4801       const char *s_end)
4802 {
4803    const char *escape;
4804
4805    escape = _escaped_char_get(s, s_end);
4806    if (escape)
4807      evas_textblock_cursor_text_prepend(cur, escape);
4808 }
4809
4810
4811 EAPI void
4812 evas_object_textblock_text_markup_set(Evas_Object *obj, const char *text)
4813 {
4814    TB_HEAD();
4815    if ((text != o->markup_text) && (o->markup_text))
4816      {
4817         free(o->markup_text);
4818         o->markup_text = NULL;
4819      }
4820    _nodes_clear(obj);
4821    if (!o->style)
4822      {
4823         if (text != o->markup_text)
4824           {
4825              if (text) o->markup_text = strdup(text);
4826           }
4827         return;
4828      }
4829    evas_textblock_cursor_paragraph_first(o->cursor);
4830
4831    evas_object_textblock_text_markup_prepend(o->cursor, text);
4832    /* Point all the cursors to the starrt */
4833      {
4834         Eina_List *l;
4835         Evas_Textblock_Cursor *data;
4836
4837         evas_textblock_cursor_paragraph_first(o->cursor);
4838         EINA_LIST_FOREACH(o->cursors, l, data)
4839            evas_textblock_cursor_paragraph_first(data);
4840      }
4841 }
4842
4843 EAPI void
4844 evas_object_textblock_text_markup_prepend(Evas_Textblock_Cursor *cur, const char *text)
4845 {
4846    Evas_Object *obj = cur->obj;
4847    TB_HEAD();
4848    if (text)
4849      {
4850         char *s, *p;
4851         char *tag_start, *tag_end, *esc_start, *esc_end;
4852
4853         tag_start = tag_end = esc_start = esc_end = NULL;
4854         p = (char *)text;
4855         s = p;
4856         /* This loop goes through all of the mark up text until it finds format
4857          * tags, escape sequences or the terminating NULL. When it finds either
4858          * of those, it appends the text found up until that point to the textblock
4859          * proccesses whatever found. It repeats itself until the termainating
4860          * NULL is reached. */
4861         for (;;)
4862           {
4863              /* If we got to the end of string or just finished/started tag
4864               * or escape sequence handling. */
4865              if ((*p == 0) ||
4866                    (tag_end) || (esc_end) ||
4867                    (tag_start) || (esc_start))
4868                {
4869                   if (tag_end)
4870                     {
4871                        /* If we reached to a tag ending, analyze the tag */
4872                        char *ttag;
4873                        size_t ttag_len = tag_end - tag_start;
4874
4875
4876                        ttag = malloc(ttag_len + 1);
4877                        if (ttag)
4878                          {
4879                             memcpy(ttag, tag_start, ttag_len);
4880                             ttag[ttag_len] = 0;
4881                             evas_textblock_cursor_format_prepend(cur, ttag);
4882                             free(ttag);
4883                          }
4884                        tag_start = tag_end = NULL;
4885                     }
4886                   else if (esc_end)
4887                     {
4888                        _prepend_escaped_char(cur, esc_start, esc_end + 1);
4889                        esc_start = esc_end = NULL;
4890                     }
4891                   else if (*p == 0)
4892                     {
4893                        _prepend_text_run(cur, s, p);
4894                        s = NULL;
4895                     }
4896                   if (*p == 0)
4897                     break;
4898                }
4899              if (*p == '<')
4900                {
4901                   if (!esc_start)
4902                     {
4903                        /* Append the text prior to this to the textblock and mark
4904                         * the start of the tag */
4905                        tag_start = p;
4906                        tag_end = NULL;
4907                        _prepend_text_run(cur, s, p);
4908                        s = NULL;
4909                     }
4910                }
4911              else if (*p == '>')
4912                {
4913                   if (tag_start)
4914                     {
4915                        tag_end = p + 1;
4916                        s = p + 1;
4917                     }
4918                }
4919              else if (*p == '&')
4920                {
4921                   if (!tag_start)
4922                     {
4923                        /* Append the text prior to this to the textblock and mark
4924                         * the start of the escape sequence */
4925                        esc_start = p;
4926                        esc_end = NULL;
4927                        _prepend_text_run(cur, s, p);
4928                        s = NULL;
4929                     }
4930                }
4931              else if (*p == ';')
4932                {
4933                   if (esc_start)
4934                     {
4935                        esc_end = p;
4936                        s = p + 1;
4937                     }
4938                }
4939              /* Unicode object replcament char */
4940              else if (!strncmp("\xEF\xBF\xBC", p, 3))
4941                {
4942                   /*FIXME: currently just remove them, maybe do something
4943                    * fancier in the future, atm it breaks if this char
4944                    * is inside <> */
4945                   _prepend_text_run(cur, s, p);
4946                   p += 2; /* it's also advanced later in this loop need +3
4947                            * in total*/
4948                   s = p + 1; /* One after the end of the replacement char */
4949                }
4950              p++;
4951           }
4952      }
4953    _evas_textblock_changed(o, obj);
4954 }
4955
4956
4957 /**
4958  * @internal
4959  * An helper function to markup get. Appends the format from fnode to the strbugf txt.
4960  *
4961  * @param o the textblock object.
4962  * @param txt the strbuf to append to.
4963  * @param fnode the format node to process.
4964  */
4965 static void
4966 _markup_get_format_append(Evas_Object_Textblock *o __UNUSED__, Eina_Strbuf *txt, Evas_Object_Textblock_Node_Format *fnode)
4967 {
4968    eina_strbuf_append_char(txt, '<');
4969      {
4970         const char *s;
4971         int pop = 0;
4972
4973         // FIXME: need to escape
4974         s = fnode->orig_format;
4975         if (*s == '-') pop = 1;
4976         while ((*s == ' ') || (*s == '+') || (*s == '-')) s++;
4977         if (pop) eina_strbuf_append_char(txt, '/');
4978         eina_strbuf_append(txt, s);
4979      }
4980    eina_strbuf_append_char(txt, '>');
4981 }
4982
4983 /**
4984  * @internal
4985  * An helper function to markup get. Appends the text in text.
4986  *
4987  * @param txt the strbuf to append to.
4988  * @param text the text to process.
4989  */
4990 static void
4991 _markup_get_text_append(Eina_Strbuf *txt, const Eina_Unicode *text)
4992 {
4993    char *p = eina_unicode_unicode_to_utf8(text, NULL);
4994    char *base = p;
4995    while (*p)
4996      {
4997         const char *escape;
4998         int adv;
4999
5000         escape = _escaped_char_match(p, &adv);
5001         if (escape)
5002           {
5003              p += adv;
5004              eina_strbuf_append(txt, escape);
5005           }
5006         else
5007           {
5008              eina_strbuf_append_char(txt, *p);
5009              p++;
5010           }
5011      }
5012    free(base);
5013 }
5014 EAPI const char *
5015 evas_object_textblock_text_markup_get(const Evas_Object *obj)
5016 {
5017    Evas_Object_Textblock_Node_Text *n;
5018    Eina_Strbuf *txt = NULL;
5019
5020    TB_HEAD_RETURN(NULL);
5021    if (o->markup_text) return(o->markup_text);
5022    txt = eina_strbuf_new();
5023    EINA_INLIST_FOREACH(o->text_nodes, n)
5024      {
5025         Evas_Object_Textblock_Node_Format *fnode;
5026         Eina_Unicode *text_base, *text;
5027         int off;
5028
5029         /* For each text node to thorugh all of it's format nodes
5030          * append text from the start to the offset of the next format
5031          * using the last format got. if needed it also creates format items
5032          * this is the core algorithm of the layout mechanism.
5033          * Skip the unicode replacement chars when there are because
5034          * we don't want to print them. */
5035         text_base = text =
5036            eina_unicode_strndup(eina_ustrbuf_string_get(n->unicode),
5037                                 eina_ustrbuf_length_get(n->unicode));
5038         fnode = n->format_node;
5039         off = 0;
5040         while (fnode && (fnode->text_node == n))
5041           {
5042              Eina_Unicode tmp_ch;
5043              off += fnode->offset;
5044              /* No need to skip on the first run */
5045              tmp_ch = text[off];
5046              text[off] = 0; /* Null terminate the part of the string */
5047              _markup_get_text_append(txt, text);
5048              _markup_get_format_append(o, txt, fnode);
5049              text[off] = tmp_ch; /* Restore the char */
5050              text += off;
5051              if (fnode->visible)
5052                {
5053                   off = -1;
5054                   text++;
5055                }
5056              else
5057                {
5058                   off = 0;
5059                }
5060              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
5061           }
5062         /* Add the rest, skip replacement */
5063         _markup_get_text_append(txt, text);
5064         free(text_base);
5065      }
5066
5067
5068    o->markup_text = eina_strbuf_string_steal(txt);
5069    eina_strbuf_free(txt);
5070    return o->markup_text;
5071 }
5072
5073 /* cursors */
5074
5075 /**
5076  * @internal
5077  * Merge the current node with the next, no need to remove PS, already
5078  * not there.
5079  *
5080  * @param o the text block object.
5081  * @param to merge into to.
5082  */
5083 static void
5084 _evas_textblock_nodes_merge(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *to)
5085 {
5086    Evas_Object_Textblock_Node_Format *itr;
5087    Evas_Object_Textblock_Node_Format *pnode;
5088    Evas_Object_Textblock_Node_Text *from;
5089    const Eina_Unicode *text;
5090    int to_len, len;
5091
5092    if (!to) return;
5093    from = _NODE_TEXT(EINA_INLIST_GET(to)->next);
5094
5095    to_len = eina_ustrbuf_length_get(to->unicode);
5096    text = eina_ustrbuf_string_get(from->unicode);
5097    len = eina_ustrbuf_length_get(from->unicode);
5098    eina_ustrbuf_append_length(to->unicode, text, len);
5099
5100    itr = from->format_node;
5101    if (itr && (itr->text_node == from))
5102      {
5103         pnode = _NODE_FORMAT(EINA_INLIST_GET(itr)->prev);
5104         if (pnode && (pnode->text_node == to))
5105           {
5106              itr->offset += to_len - _evas_textblock_node_format_pos_get(pnode);
5107           }
5108         else
5109           {
5110              itr->offset += to_len;
5111           }
5112      }
5113
5114    while (itr && (itr->text_node == from))
5115      {
5116         itr->text_node = to;
5117         itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
5118      }
5119    if (!to->format_node || (to->format_node->text_node != to))
5120      {
5121         to->format_node = from->format_node;
5122      }
5123
5124    /* When it comes to how we handle it, merging is like removing both nodes
5125     * and creating a new one, se we need to do the needed cleanups. */
5126    if (to->par)
5127       to->par->text_node = NULL;
5128    to->par = NULL;
5129
5130    to->is_new = EINA_TRUE;
5131
5132    _evas_textblock_cursors_set_node(o, from, to);
5133    _evas_textblock_node_text_remove(o, from);
5134 }
5135
5136 /**
5137  * @internal
5138  * Merge the current node with the next, no need to remove PS, already
5139  * not there.
5140  *
5141  * @param cur the cursor that points to the current node
5142  */
5143 static void
5144 _evas_textblock_cursor_nodes_merge(Evas_Textblock_Cursor *cur)
5145 {
5146    Evas_Object_Textblock_Node_Text *nnode;
5147    Evas_Object_Textblock *o;
5148    int len;
5149    if (!cur) return;
5150
5151    len = eina_ustrbuf_length_get(cur->node->unicode);
5152
5153    o = (Evas_Object_Textblock *)(cur->obj->object_data);
5154    nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
5155    _evas_textblock_nodes_merge(o, cur->node);
5156    _evas_textblock_cursors_update_offset(cur, nnode, 0, len);
5157    _evas_textblock_cursors_set_node(o, nnode, cur->node);
5158    if (nnode == o->cursor->node)
5159      {
5160         o->cursor->node = cur->node;
5161         o->cursor->pos += len;
5162      }
5163 }
5164
5165 /**
5166  * @internal
5167  * Return the format at a specific position.
5168  *
5169  * @param cur the cursor to the position.
5170  * @return the format node at the specific position or NULL if not found.
5171  */
5172 static Evas_Object_Textblock_Node_Format *
5173 _evas_textblock_cursor_node_format_at_pos_get(const Evas_Textblock_Cursor *cur)
5174 {
5175    Evas_Object_Textblock_Node_Format *node;
5176    Evas_Object_Textblock_Node_Format *itr;
5177    int position = 0;
5178
5179    if (!cur->node) return NULL;
5180
5181    node = cur->node->format_node;
5182    if (!node) return NULL;
5183    /* If there is no exclusive format node to this paragraph return the
5184     * previous's node */
5185    /* Find the main format node */
5186    EINA_INLIST_FOREACH(node, itr)
5187      {
5188         if (itr->text_node != cur->node)
5189           {
5190              return NULL;
5191           }
5192         if ((position + itr->offset) == cur->pos)
5193           {
5194              return itr;
5195           }
5196         position += itr->offset;
5197      }
5198    return NULL;
5199 }
5200
5201 /**
5202  * @internal
5203  * Return the last format node at the position of the format node n.
5204  *
5205  * @param n a format node at the position.
5206  * @return the last format node at the position of n.
5207  */
5208 static Evas_Object_Textblock_Node_Format *
5209 _evas_textblock_node_format_last_at_off(const Evas_Object_Textblock_Node_Format *n)
5210 {
5211    const Evas_Object_Textblock_Node_Format *nnode;
5212    const Evas_Object_Textblock_Node_Text *tnode;
5213    if (!n) return NULL;
5214    nnode = n;
5215    tnode = n->text_node;
5216    do
5217      {
5218         n = nnode;
5219         nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
5220      }
5221    while (nnode && (nnode->text_node == tnode) && (nnode->offset == 0));
5222
5223    return (Evas_Object_Textblock_Node_Format *) n;
5224 }
5225
5226 /**
5227  * @internal
5228  * Returns the visible format at a specific location.
5229  *
5230  * @param n a format at the specific position.
5231  * @return the format node at the specific position or NULL if not found.
5232  */
5233 static Evas_Object_Textblock_Node_Format *
5234 _evas_textblock_node_visible_at_pos_get(const Evas_Object_Textblock_Node_Format *n)
5235 {
5236    const Evas_Object_Textblock_Node_Format *nnode;
5237    if (!n) return NULL;
5238    /* The visible format is the last one, because it inserts a replacement
5239     * char that advances the next formats. */
5240
5241    nnode = n;
5242    do
5243      {
5244         n = nnode;
5245         if (n->visible) return (Evas_Object_Textblock_Node_Format *) n;
5246         nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
5247      }
5248    while (nnode && (nnode->offset == 0));
5249
5250    return NULL;
5251 }
5252
5253 /**
5254  * @internal
5255  * Return the last format that applies to a specific cursor or at the specific
5256  * position the cursor points to. This means either a cursor at or before the
5257  * position of the cursor in the text node is returned or the previous's text
5258  * node's format node.
5259  *
5260  * @param cur the position to look at.
5261  * @return the format node found.
5262  */
5263 static Evas_Object_Textblock_Node_Format *
5264 _evas_textblock_cursor_node_format_before_or_at_pos_get(const Evas_Textblock_Cursor *cur)
5265 {
5266    Evas_Object_Textblock_Node_Format *node, *pitr = NULL;
5267    Evas_Object_Textblock_Node_Format *itr;
5268    size_t position = 0;
5269
5270    if (!cur->node) return NULL;
5271
5272    node = cur->node->format_node;
5273    if (!node) return NULL;
5274    /* If there is no exclusive format node to this paragraph return the
5275     * previous's node */
5276    if (node->text_node != cur->node)
5277      {
5278         return node;
5279      }
5280    else if (node->offset > cur->pos)
5281      {
5282         return _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5283      }
5284    /* Find the main format node */
5285    pitr = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5286    EINA_INLIST_FOREACH(node, itr)
5287      {
5288         if ((itr->text_node != cur->node) ||
5289             ((position + itr->offset) > cur->pos))
5290           {
5291              return pitr;
5292           }
5293         else if ((position + itr->offset) == cur->pos)
5294           {
5295              return itr;
5296           }
5297         pitr = itr;
5298         position += itr->offset;
5299      }
5300    return pitr;
5301 }
5302
5303 /**
5304  * @internal
5305  * Find the layout item and line that match the cursor.
5306  *
5307  * @param cur the cursor we are currently at. - NOT NULL.
5308  * @param[out] lnr the line found - not null.
5309  * @param[out] itr the item found - not null.
5310  * @return EINA_TRUE if we matched the previous format, EINA_FALSE otherwise.
5311  */
5312 static Eina_Bool
5313 _find_layout_item_match(const Evas_Textblock_Cursor *cur, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Item **itr)
5314 {
5315    Evas_Textblock_Cursor cur2;
5316    Eina_Bool previous_format = EINA_FALSE;
5317
5318    cur2.obj = cur->obj;
5319    evas_textblock_cursor_copy(cur, &cur2);
5320    if (cur2.pos > 0)
5321      {
5322         cur2.pos--;
5323      }
5324
5325    if (_evas_textblock_cursor_is_at_the_end(cur) &&
5326             evas_textblock_cursor_format_is_visible_get(&cur2))
5327      {
5328         _find_layout_item_line_match(cur2.obj, cur2.node, cur2.pos, lnr, itr);
5329         previous_format = EINA_TRUE;
5330      }
5331    else
5332      {
5333         _find_layout_item_line_match(cur->obj, cur->node, cur->pos, lnr, itr);
5334      }
5335    return previous_format;
5336 }
5337
5338 EAPI Evas_Textblock_Cursor *
5339 evas_object_textblock_cursor_get(const Evas_Object *obj)
5340 {
5341    TB_HEAD_RETURN(NULL);
5342    return o->cursor;
5343 }
5344
5345 EAPI Evas_Textblock_Cursor *
5346 evas_object_textblock_cursor_new(const Evas_Object *obj)
5347 {
5348    Evas_Textblock_Cursor *cur;
5349
5350    TB_HEAD_RETURN(NULL);
5351    cur = calloc(1, sizeof(Evas_Textblock_Cursor));
5352    cur->obj = (Evas_Object *) obj;
5353    cur->node = o->text_nodes;
5354    cur->pos = 0;
5355
5356    o->cursors = eina_list_append(o->cursors, cur);
5357    return cur;
5358 }
5359
5360 EAPI void
5361 evas_textblock_cursor_free(Evas_Textblock_Cursor *cur)
5362 {
5363    Evas_Object_Textblock *o;
5364
5365    if (!cur) return;
5366    o = (Evas_Object_Textblock *)(cur->obj->object_data);
5367    if (cur == o->cursor) return;
5368    o->cursors = eina_list_remove(o->cursors, cur);
5369    free(cur);
5370 }
5371
5372 EAPI Eina_Bool
5373 evas_textblock_cursor_is_format(const Evas_Textblock_Cursor *cur)
5374 {
5375    if (!cur || !cur->node) return EINA_FALSE;
5376    if (evas_textblock_cursor_format_is_visible_get(cur)) return EINA_TRUE;
5377    return (_evas_textblock_cursor_node_format_at_pos_get(cur)) ?
5378       EINA_TRUE : EINA_FALSE;
5379 }
5380
5381 EAPI const Eina_List *
5382 evas_textblock_node_format_list_get(const Evas_Object *obj, const char *anchor)
5383 {
5384    TB_HEAD_RETURN(NULL);
5385    if (!strcmp(anchor, "a"))
5386       return o->anchors_a;
5387    else if (!strcmp(anchor, "item"))
5388       return o->anchors_item;
5389
5390    return NULL;
5391 }
5392
5393 EAPI const Evas_Object_Textblock_Node_Format *
5394 evas_textblock_node_format_first_get(const Evas_Object *obj)
5395 {
5396    TB_HEAD_RETURN(NULL);
5397    return o->format_nodes;
5398 }
5399
5400 EAPI const Evas_Object_Textblock_Node_Format *
5401 evas_textblock_node_format_last_get(const Evas_Object *obj)
5402 {
5403    TB_HEAD_RETURN(NULL);
5404    if (o->format_nodes)
5405      {
5406         return _NODE_FORMAT(EINA_INLIST_GET(o->format_nodes)->last);
5407      }
5408    return NULL;
5409 }
5410
5411 EAPI const Evas_Object_Textblock_Node_Format *
5412 evas_textblock_node_format_next_get(const Evas_Object_Textblock_Node_Format *n)
5413 {
5414    return _NODE_FORMAT(EINA_INLIST_GET(n)->next);
5415 }
5416
5417 EAPI const Evas_Object_Textblock_Node_Format *
5418 evas_textblock_node_format_prev_get(const Evas_Object_Textblock_Node_Format *n)
5419 {
5420    return _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
5421 }
5422
5423 EAPI void
5424 evas_textblock_node_format_remove_pair(Evas_Object *obj,
5425       Evas_Object_Textblock_Node_Format *n)
5426 {
5427    Evas_Object_Textblock_Node_Text *tnode1;
5428    Evas_Object_Textblock_Node_Format *fmt, *found_node = NULL;
5429    Eina_List *fstack = NULL;
5430    TB_HEAD();
5431
5432    if (!n) return;
5433
5434    fmt = n;
5435
5436    do
5437      {
5438         const char *fstr = fmt->orig_format;
5439
5440         if (fstr && (*fstr == '+'))
5441           {
5442              fstack = eina_list_prepend(fstack, fmt);
5443           }
5444         else if (fstr && (*fstr == '-'))
5445           {
5446              size_t fstr_len;
5447              /* Skip the '-' */
5448              fstr++;
5449              fstr_len = strlen(fstr);
5450              /* Generic popper, just pop */
5451              if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
5452                {
5453                   fstack = eina_list_remove_list(fstack, fstack);
5454                   if (!fstack)
5455                     {
5456                        found_node = fmt;
5457                        goto found;
5458                     }
5459                }
5460              /* Find the matching format and pop it, if the matching format
5461               * is out format, i.e the last one, pop and break. */
5462              else
5463                {
5464                   Eina_List *i;
5465                   Evas_Object_Textblock_Node_Format *fnode;
5466                   EINA_LIST_FOREACH(fstack, i, fnode)
5467                     {
5468                        if (_FORMAT_IS_CLOSER_OF(
5469                                 fnode->orig_format, fstr, fstr_len))
5470                          {
5471                             /* Last one, this is our item! */
5472                             if (!eina_list_next(i))
5473                               {
5474                                  found_node = fmt;
5475                                  goto found;
5476                               }
5477                             fstack = eina_list_remove_list(fstack, i);
5478                             break;
5479                          }
5480                     }
5481                }
5482           }
5483
5484         fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
5485      }
5486    while (fmt && fstack);
5487
5488 found:
5489
5490    fstack = eina_list_free(fstack);
5491
5492    if (n->visible)
5493      {
5494         size_t ind = _evas_textblock_node_format_pos_get(n);
5495         const char *format = n->format;
5496         Evas_Textblock_Cursor cur;
5497         cur.obj = obj;
5498
5499         eina_ustrbuf_remove(n->text_node->unicode, ind, ind + 1);
5500         if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
5501           {
5502              evas_textblock_cursor_at_format_set(&cur, n);
5503              _evas_textblock_cursor_nodes_merge(&cur);
5504           }
5505         _evas_textblock_cursors_update_offset(&cur, n->text_node, ind, -1);
5506      }
5507    tnode1 = n->text_node;
5508    _evas_textblock_node_format_remove(o, n, 0);
5509    if (found_node && (found_node != n))
5510      {
5511         Evas_Object_Textblock_Node_Text *tnode2;
5512         tnode2 = found_node->text_node;
5513         /* found_node can never be visible! (it's the closing format) */
5514         _evas_textblock_node_format_remove(o, found_node, 0);
5515
5516         /* FIXME: Should be unified in the layout, for example, added to a list
5517          * that checks this kind of removals. But until then, this is very fast
5518          * and works. */
5519         /* Mark all the text nodes in between the removed formats as dirty. */
5520         while (tnode1)
5521           {
5522              tnode1->dirty = EINA_TRUE;
5523              if (tnode1 == tnode2)
5524                 break;
5525              tnode1 =
5526                 _NODE_TEXT(EINA_INLIST_GET(tnode1)->next);
5527           }
5528      }
5529
5530    _evas_textblock_changed(o, obj);
5531 }
5532
5533 EAPI void
5534 evas_textblock_cursor_paragraph_first(Evas_Textblock_Cursor *cur)
5535 {
5536    Evas_Object_Textblock *o;
5537    if (!cur) return;
5538    o = (Evas_Object_Textblock *)(cur->obj->object_data);
5539    cur->node = o->text_nodes;
5540    cur->pos = 0;
5541
5542 }
5543
5544 EAPI void
5545 evas_textblock_cursor_paragraph_last(Evas_Textblock_Cursor *cur)
5546 {
5547    Evas_Object_Textblock *o;
5548    Evas_Object_Textblock_Node_Text *node;
5549
5550    if (!cur) return;
5551    o = (Evas_Object_Textblock *)(cur->obj->object_data);
5552    node = o->text_nodes;
5553    if (node)
5554      {
5555         node = _NODE_TEXT(EINA_INLIST_GET(node)->last);
5556         cur->node = node;
5557         cur->pos = 0;
5558
5559         evas_textblock_cursor_paragraph_char_last(cur);
5560      }
5561    else
5562      {
5563         cur->node = NULL;
5564         cur->pos = 0;
5565
5566      }
5567 }
5568
5569 EAPI Eina_Bool
5570 evas_textblock_cursor_paragraph_next(Evas_Textblock_Cursor *cur)
5571 {
5572    if (!cur) return EINA_FALSE;
5573    if (!cur->node) return EINA_FALSE;
5574    /* If there is a current text node, return the next text node (if exists)
5575     * otherwise, just return False. */
5576    if (cur->node)
5577      {
5578         Evas_Object_Textblock_Node_Text *nnode;
5579         nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
5580         if (nnode)
5581           {
5582              cur->node = nnode;
5583              cur->pos = 0;
5584
5585              return EINA_TRUE;
5586           }
5587      }
5588    return EINA_FALSE;
5589 }
5590
5591 EAPI Eina_Bool
5592 evas_textblock_cursor_paragraph_prev(Evas_Textblock_Cursor *cur)
5593 {
5594    Evas_Object_Textblock_Node_Text *node;
5595    if (!cur) return EINA_FALSE;
5596    if (!cur->node) return EINA_FALSE;
5597    /* If the current node is a text node, just get the prev if any,
5598     * if it's a format, get the current text node out of the format and return
5599     * the prev text node if any. */
5600    node = cur->node;
5601    /* If there is a current text node, return the prev text node
5602     * (if exists) otherwise, just return False. */
5603    if (node)
5604      {
5605         Evas_Object_Textblock_Node_Text *pnode;
5606         pnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->prev);
5607         if (pnode)
5608           {
5609              cur->node = pnode;
5610              evas_textblock_cursor_paragraph_char_last(cur);
5611              return EINA_TRUE;
5612           }
5613      }
5614    return EINA_FALSE;
5615 }
5616
5617 EAPI void
5618 evas_textblock_cursor_set_at_format(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *n)
5619 {
5620    evas_textblock_cursor_at_format_set(cur, n);
5621 }
5622
5623 EAPI Eina_Bool
5624 evas_textblock_cursor_format_next(Evas_Textblock_Cursor *cur)
5625 {
5626    Evas_Object_Textblock_Node_Format *node;
5627
5628    if (!cur) return EINA_FALSE;
5629    if (!cur->node) return EINA_FALSE;
5630    /* If the current node is a format node, just get the next if any,
5631     * if it's a text, get the current format node out of the text and return
5632     * the next format node if any. */
5633    node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
5634    node = _evas_textblock_node_format_last_at_off(node);
5635    if (!node)
5636      {
5637         if (cur->node->format_node)
5638           {
5639              cur->pos = _evas_textblock_node_format_pos_get(node);
5640              return EINA_TRUE;
5641           }
5642      }
5643    /* If there is a current text node, return the next format node (if exists)
5644     * otherwise, just return False. */
5645    else
5646      {
5647         Evas_Object_Textblock_Node_Format *nnode;
5648         nnode = _NODE_FORMAT(EINA_INLIST_GET(node)->next);
5649         if (nnode)
5650           {
5651              cur->node = nnode->text_node;
5652              cur->pos = _evas_textblock_node_format_pos_get(nnode);
5653
5654              return EINA_TRUE;
5655           }
5656      }
5657    return EINA_FALSE;
5658 }
5659
5660 EAPI Eina_Bool
5661 evas_textblock_cursor_format_prev(Evas_Textblock_Cursor *cur)
5662 {
5663    const Evas_Object_Textblock_Node_Format *node;
5664    if (!cur) return EINA_FALSE;
5665    if (!cur->node) return EINA_FALSE;
5666    node = evas_textblock_cursor_format_get(cur);
5667    if (!node)
5668      {
5669         node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
5670         if (node)
5671           {
5672              cur->node = node->text_node;
5673              cur->pos = _evas_textblock_node_format_pos_get(node);
5674
5675              return EINA_TRUE;
5676           }
5677      }
5678    /* If there is a current text node, return the next text node (if exists)
5679     * otherwise, just return False. */
5680    if (node)
5681      {
5682         Evas_Object_Textblock_Node_Format *pnode;
5683         pnode = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
5684         if (pnode)
5685           {
5686              cur->node = pnode->text_node;
5687              cur->pos = _evas_textblock_node_format_pos_get(pnode);
5688
5689              return EINA_TRUE;
5690           }
5691      }
5692    return EINA_FALSE;
5693 }
5694
5695 EAPI Eina_Bool
5696 evas_textblock_cursor_char_next(Evas_Textblock_Cursor *cur)
5697 {
5698    int ind;
5699    const Eina_Unicode *text;
5700
5701    if (!cur) return EINA_FALSE;
5702    if (!cur->node) return EINA_FALSE;
5703
5704    ind = cur->pos;
5705    text = eina_ustrbuf_string_get(cur->node->unicode);
5706    if (text[ind]) ind++;
5707    /* Only allow pointing a null if it's the last paragraph.
5708     * because we don't have a PS there. */
5709    if (text[ind])
5710      {
5711         cur->pos = ind;
5712         return EINA_TRUE;
5713      }
5714    else
5715      {
5716         if (!evas_textblock_cursor_paragraph_next(cur))
5717           {
5718              /* If we already were at the end, that means we don't have
5719               * where to go next we should return FALSE */
5720              if (cur->pos == (size_t) ind)
5721                return EINA_FALSE;
5722
5723              cur->pos = ind;
5724              return EINA_TRUE;
5725           }
5726         else
5727           {
5728              return EINA_TRUE;
5729           }
5730      }
5731 }
5732
5733 EAPI Eina_Bool
5734 evas_textblock_cursor_char_prev(Evas_Textblock_Cursor *cur)
5735 {
5736    if (!cur) return EINA_FALSE;
5737    if (!cur->node) return EINA_FALSE;
5738
5739    if (cur->pos != 0)
5740      {
5741         cur->pos--;
5742         return EINA_TRUE;
5743      }
5744    return evas_textblock_cursor_paragraph_prev(cur);
5745 }
5746
5747 EAPI void
5748 evas_textblock_cursor_paragraph_char_first(Evas_Textblock_Cursor *cur)
5749 {
5750    if (!cur) return;
5751    cur->pos = 0;
5752
5753 }
5754
5755 EAPI void
5756 evas_textblock_cursor_paragraph_char_last(Evas_Textblock_Cursor *cur)
5757 {
5758    int ind;
5759
5760    if (!cur) return;
5761    if (!cur->node) return;
5762    ind = eina_ustrbuf_length_get(cur->node->unicode);
5763    /* If it's not the last paragraph, go back one, because we want to point
5764     * to the PS, not the NULL */
5765    if (EINA_INLIST_GET(cur->node)->next)
5766       ind--;
5767
5768    if (ind >= 0)
5769       cur->pos = ind;
5770    else
5771       cur->pos = 0;
5772
5773 }
5774
5775 EAPI void
5776 evas_textblock_cursor_line_char_first(Evas_Textblock_Cursor *cur)
5777 {
5778    Evas_Object_Textblock *o;
5779    Evas_Object_Textblock_Line *ln = NULL;
5780    Evas_Object_Textblock_Item *it = NULL;
5781
5782    if (!cur) return;
5783    if (!cur->node) return;
5784    o = (Evas_Object_Textblock *)(cur->obj->object_data);
5785    if (!o->formatted.valid) _relayout(cur->obj);
5786
5787    _find_layout_item_match(cur, &ln, &it);
5788
5789    if (!ln) return;
5790    if (ln->items)
5791      {
5792         Evas_Object_Textblock_Item *i;
5793         it = ln->items;
5794         EINA_INLIST_FOREACH(ln->items, i)
5795           {
5796              if (it->text_pos > i->text_pos)
5797                {
5798                   it = i;
5799                }
5800           }
5801      }
5802    if (it)
5803      {
5804         cur->pos = it->text_pos;
5805         cur->node = it->text_node;
5806      }
5807 }
5808
5809 EAPI void
5810 evas_textblock_cursor_line_char_last(Evas_Textblock_Cursor *cur)
5811 {
5812    Evas_Object_Textblock *o;
5813    Evas_Object_Textblock_Line *ln = NULL;
5814    Evas_Object_Textblock_Item *it = NULL;
5815
5816    if (!cur) return;
5817    if (!cur->node) return;
5818    o = (Evas_Object_Textblock *)(cur->obj->object_data);
5819    if (!o->formatted.valid) _relayout(cur->obj);
5820
5821    _find_layout_item_match(cur, &ln, &it);
5822
5823    if (!ln) return;
5824    if (ln->items)
5825      {
5826         Evas_Object_Textblock_Item *i;
5827         it = ln->items;
5828         EINA_INLIST_FOREACH(ln->items, i)
5829           {
5830              if (it->text_pos < i->text_pos)
5831                {
5832                   it = i;
5833                }
5834           }
5835      }
5836    if (it)
5837      {
5838         size_t ind;
5839
5840         cur->node = it->text_node;
5841         cur->pos = it->text_pos;
5842         if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
5843           {
5844              ind = _ITEM_TEXT(it)->text_props.text_len - 1;
5845              if (!IS_AT_END(_ITEM_TEXT(it), ind)) ind++;
5846              cur->pos += ind;
5847           }
5848         else if (!EINA_INLIST_GET(ln)->next && !EINA_INLIST_GET(ln->par)->next)
5849           {
5850              cur->pos++;
5851           }
5852      }
5853 }
5854
5855 /**
5856  * @internal
5857  * checks if a format (as a string) is visible/changes format and sets the
5858  * fnode properties accordingly.
5859  *
5860  * @param fnode the format node
5861  * @param s the string.
5862  */
5863 static void
5864 _evas_textblock_format_is_visible(Evas_Object_Textblock_Node_Format *fnode,
5865       const char *s)
5866 {
5867    const char *item;
5868    Eina_Bool is_opener = EINA_TRUE;
5869
5870    fnode->visible = fnode->format_change = EINA_FALSE;
5871    fnode->anchor = ANCHOR_NONE;
5872    if (!s) return;
5873
5874    if (s[0] == '+' || s[0] == '-')
5875      {
5876         is_opener = (s[0] == '+');
5877         s++;
5878         fnode->format_change = EINA_TRUE;
5879      }
5880
5881    while ((item = _format_parse(&s)))
5882      {
5883         int itlen = s - item;
5884         /* We care about all of the formats even after a - except for
5885          * item which we don't care after a - because it's just a standard
5886          * closing */
5887         if ((!strncmp(item, "\n", itlen) || !strncmp(item, "\\n", itlen)) ||
5888               (!strncmp(item, "\t", itlen) || !strncmp(item, "\\t", itlen)) ||
5889               (!strncmp(item, "ps", itlen) && (itlen >= 2)) ||
5890               (!strncmp(item, "item", itlen) && (itlen >= 4) && is_opener))
5891           {
5892              fnode->visible = EINA_TRUE;
5893           }
5894
5895         if (is_opener && !strncmp(item, "a", itlen))
5896           {
5897              fnode->anchor = ANCHOR_A;
5898           }
5899         else if (is_opener && !strncmp(item, "item", itlen) && (itlen >= 4))
5900           {
5901              fnode->anchor = ANCHOR_ITEM;
5902           }
5903      }
5904 }
5905
5906 /**
5907  * Sets the cursor to the position of where the fmt points to.
5908  *
5909  * @param cur the cursor to update.
5910  * @param fmt the format to set according to.
5911  * @return nothing.
5912  */
5913 static void __UNUSED__
5914 _evas_textblock_cursor_node_text_at_format(Evas_Textblock_Cursor *cur, Evas_Object_Textblock_Node_Format *fmt)
5915 {
5916    Evas_Object_Textblock_Node_Text *text;
5917    Evas_Object_Textblock_Node_Format *base_format;
5918    Evas_Object_Textblock_Node_Format *itr;
5919    size_t position = 0;
5920
5921    if (!cur || !fmt) return;
5922    /* Find the main format node */
5923    text = fmt->text_node;
5924    cur->node = text;
5925    base_format = text->format_node;
5926    EINA_INLIST_FOREACH(base_format, itr)
5927      {
5928         if (itr == fmt)
5929           {
5930              break;
5931           }
5932         position += itr->offset;
5933      }
5934    cur->pos = position;
5935
5936 }
5937
5938
5939 /**
5940  * @internal
5941  * Remove pairs of + and - formats and also remove formats without + or -
5942  * i.e formats that pair to themselves. Only removes invisible formats
5943  * that pair themselves, if you want to remove invisible formats that pair
5944  * themselves, please first change fmt->visible to EINA_FALSE.
5945  *
5946  * @param o the textblock object.
5947  * @param fmt the current format.
5948  */
5949 static void
5950 _evas_textblock_node_format_remove_matching(Evas_Object_Textblock *o,
5951       Evas_Object_Textblock_Node_Format *fmt)
5952 {
5953    Evas_Object_Textblock_Node_Text *tnode;
5954    Eina_List *formats = NULL;
5955    size_t offset = 0;
5956
5957    if (!fmt) return;
5958
5959    tnode = fmt->text_node;
5960
5961    do
5962      {
5963         Evas_Object_Textblock_Node_Format *nnode;
5964         const char *fstr = fmt->orig_format;
5965
5966         nnode = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
5967         if (nnode)
5968           {
5969              offset = nnode->offset;
5970           }
5971
5972
5973         if (fstr && (*fstr == '+'))
5974           {
5975              formats = eina_list_prepend(formats, fmt);
5976           }
5977         else if (fstr && (*fstr == '-'))
5978           {
5979              Evas_Object_Textblock_Node_Format *fnode;
5980              size_t fstr_len;
5981              /* Skip the '-' */
5982              fstr++;
5983              fstr_len = strlen(fstr);
5984              /* Generic popper, just pop */
5985              if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
5986                {
5987                   fnode = eina_list_data_get(formats);
5988                   formats = eina_list_remove_list(formats, formats);
5989                   _evas_textblock_node_format_remove(o, fnode, 0);
5990                   _evas_textblock_node_format_remove(o, fmt, 0);
5991                }
5992              /* Find the matching format and pop it, if the matching format
5993               * is our format, i.e the last one, pop and break. */
5994              else
5995                {
5996                   Eina_List *i, *next;
5997                   EINA_LIST_FOREACH_SAFE(formats, i, next, fnode)
5998                     {
5999                        if (_FORMAT_IS_CLOSER_OF(
6000                                 fnode->orig_format, fstr, fstr_len))
6001                          {
6002                             fnode = eina_list_data_get(i);
6003                             formats = eina_list_remove_list(formats, i);
6004                             _evas_textblock_node_format_remove(o, fnode, 0);
6005                             _evas_textblock_node_format_remove(o, fmt, 0);
6006                             break;
6007                          }
6008                     }
6009                }
6010           }
6011         else if (!fmt->visible)
6012           {
6013              _evas_textblock_node_format_remove(o, fmt, 0);
6014           }
6015         fmt = nnode;
6016      }
6017    while (fmt && (offset == 0) && (fmt->text_node == tnode));
6018    eina_list_free(formats);
6019 }
6020 /**
6021  * @internal
6022  * Add the offset (may be negative) to the first node after fmt which is
6023  * pointing to the text node tnode or to o->format_nodes if fmt is null
6024  * and it points to tnode.
6025  *
6026  * @param o the textblock object.
6027  * @param tnode the text node the format should point to.
6028  * @param fmt the current format.
6029  * @param offset the offest to add (may be negative).
6030  */
6031 static void
6032 _evas_textblock_node_format_adjust_offset(Evas_Object_Textblock *o,
6033       Evas_Object_Textblock_Node_Text *tnode,
6034       Evas_Object_Textblock_Node_Format *fmt, int offset)
6035 {
6036    if (fmt)
6037      {
6038         fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
6039      }
6040    else
6041      {
6042         fmt = o->format_nodes;
6043      }
6044    if (fmt && (tnode == fmt->text_node))
6045      {
6046         fmt->offset += offset;
6047      }
6048 }
6049
6050 /**
6051  * @internal
6052  * Removes a format node updating the offset of the next format node and the
6053  * text nodes pointing to this node.
6054  *
6055  * @param o the textblock object.
6056  * @param n the fromat node to remove
6057  */
6058 static void
6059 _evas_textblock_node_format_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n, int visible_adjustment)
6060 {
6061    /* Update the text nodes about the change */
6062      {
6063         Evas_Object_Textblock_Node_Format *nnode;
6064         nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->next);
6065         /* If there's a next node that belongs to the same text node
6066          * and the curret node was the main one, advance the format node */
6067         if (nnode && (nnode->text_node == n->text_node))
6068           {
6069              if (nnode->text_node->format_node == n)
6070                {
6071                   nnode->text_node->format_node = nnode;
6072                }
6073           }
6074         else
6075           {
6076              Evas_Object_Textblock_Node_Text *tnode;
6077              /* If there's no next one update the text nodes */
6078              nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
6079              tnode = n->text_node;
6080              /* Even if it's not the current text_node's main node
6081               * it can still be the next's. */
6082              if (tnode && (tnode->format_node != n))
6083                {
6084                   tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
6085                }
6086              while (tnode && (tnode->format_node == n))
6087                {
6088                   tnode->format_node = nnode;
6089                   tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
6090                }
6091           }
6092      }
6093    _evas_textblock_node_format_adjust_offset(o, n->text_node, n,
6094          n->offset - visible_adjustment);
6095
6096    o->format_nodes = _NODE_FORMAT(eina_inlist_remove(
6097            EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n)));
6098    _evas_textblock_node_format_free(o, n);
6099 }
6100
6101 /**
6102  * @internal
6103  * Sets all the offsets of the format nodes between start and end in the text
6104  * node n to 0 and sets visibility to EINA_FALSE.
6105  * If end == -1 end means the end of the string.
6106  * Assumes there is a prev node or the current node will be preserved.
6107  *
6108  * @param n the text node the positinos refer to.
6109  * @param start the start of where to delete from.
6110  * @param end the end of the section to delete, if end == -1 it means the end of the string.
6111  * @returns #EINA_TRUE if removed a PS, false otherwise.
6112  */
6113 static Eina_Bool
6114 _evas_textblock_node_text_adjust_offsets_to_start(Evas_Object_Textblock *o,
6115       Evas_Object_Textblock_Node_Text *n, size_t start, int end)
6116 {
6117    Evas_Object_Textblock_Node_Format *last_node, *itr;
6118    Evas_Object_Textblock_Node_Text *new_node;
6119    int use_end = 1;
6120    int delta = 0;
6121    int first = 1;
6122    int update_format_node;
6123    size_t pos = 0;
6124    int orig_end;
6125
6126    itr = n->format_node;
6127    if (!itr || (itr->text_node != n)) return EINA_FALSE;
6128
6129    orig_end = end;
6130    if ((end < 0) || ((size_t) end == eina_ustrbuf_length_get(n->unicode)))
6131      {
6132         use_end = 0;
6133      }
6134    else if (end > 0)
6135      {
6136         /* We don't want the last one */
6137         end--;
6138      }
6139
6140    /* If we are not removing the text node, all should stay in this text
6141     * node, otherwise, everything should move to the previous node */
6142    if ((start == 0) && !use_end)
6143      {
6144         new_node = _NODE_TEXT(EINA_INLIST_GET(n)->prev);
6145         if (!new_node)
6146           {
6147              new_node = n;
6148           }
6149      }
6150    else
6151      {
6152         new_node = n;
6153      }
6154
6155    /* Find the first node after start */
6156    while (itr && (itr->text_node == n))
6157      {
6158         pos += itr->offset;
6159         if (pos >= start)
6160           {
6161              break;
6162           }
6163         itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6164      }
6165
6166    if (!itr || (itr->text_node != n))
6167      {
6168         return EINA_FALSE;
6169      }
6170
6171    update_format_node = ((itr == n->format_node) && (new_node != n));
6172    delta = orig_end - pos;
6173    itr->offset -= pos - start;
6174
6175    while (itr && (itr->text_node == n))
6176      {
6177         last_node = itr;
6178         itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6179
6180         if (!first)
6181           {
6182              pos += last_node->offset;
6183           }
6184
6185         /* start is negative when this gets relevant */
6186         if (use_end && (pos > (size_t) end))
6187           {
6188              last_node->offset -= delta;
6189              break;
6190           }
6191
6192         delta = orig_end - pos;
6193         if (!first)
6194           {
6195              last_node->offset = 0;
6196           }
6197         else
6198           {
6199              first = 0;
6200           }
6201         last_node->visible = EINA_FALSE;
6202
6203         if (!itr || (itr && (itr->text_node != n)))
6204           {
6205              /* Remove the PS, and return since it's the end of the node */
6206              if (_IS_PARAGRAPH_SEPARATOR(o, last_node->format))
6207                {
6208                   _evas_textblock_node_format_remove(o, last_node, 0);
6209                   return EINA_TRUE;
6210                }
6211
6212           }
6213         last_node->text_node = new_node;
6214         if (update_format_node)
6215           {
6216              n->format_node = last_node;
6217           }
6218      }
6219
6220    return EINA_FALSE;
6221 }
6222
6223 /**
6224  * @internal
6225  * Removes all the format nodes between start and end in the text node n.
6226  * This function updates the offset of the next format node and the
6227  * text nodes pointing to it. if end == -1 end means the end of the string.
6228  *
6229  * @param o the textblock object.
6230  * @param n the text node the positinos refer to.
6231  * @param start the start of where to delete from.
6232  * @param end the end of the section to delete, if end == -1 it means the end of the string.
6233  */
6234 static void
6235 _evas_textblock_node_text_remove_formats_between(Evas_Object_Textblock *o,
6236       Evas_Object_Textblock_Node_Text *n, int start, int end)
6237 {
6238    Evas_Object_Textblock_Node_Format *itr;
6239    int use_end = 1;
6240    int offset = end - start;
6241    itr = n->format_node;
6242
6243    if (itr)
6244      start -= itr->offset;
6245    if (offset < 0) offset = 0;
6246    if (end < 0) use_end = 0;
6247    while (itr && (itr->text_node == n))
6248      {
6249         Evas_Object_Textblock_Node_Format *nnode;
6250         int tmp_offset = 0;
6251
6252         /* start is negative when this gets relevant */
6253         if ((offset + start < 0) && use_end)
6254           {
6255              break;
6256           }
6257         nnode = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6258         if (nnode)
6259           {
6260              tmp_offset = nnode->offset;
6261           }
6262         if (start <= 0)
6263           {
6264              /* Don't do visible adjustments because we are removing the visual
6265               * chars anyway and taking those into account */
6266              _evas_textblock_node_format_remove(o, itr, 0);
6267           }
6268         start -= tmp_offset;
6269         itr = nnode;
6270      }
6271 }
6272
6273 /**
6274  * @internal
6275  * Returns the first format in the range between start and end in the textblock
6276  * n.
6277  *
6278  * @param o the textblock object.
6279  * @param n the text node the positinos refer to.
6280  * @param start the start of where to delete from.
6281  * @param end the end of the section to delete, if end == -1 it means the end of the string.
6282  */
6283 static Evas_Object_Textblock_Node_Format *
6284 _evas_textblock_node_text_get_first_format_between(
6285       Evas_Object_Textblock_Node_Text *n, int start, int end)
6286 {
6287    Evas_Object_Textblock_Node_Format *itr;
6288    int use_end = 1;
6289    itr = n->format_node;
6290    if (end < 0) use_end = 0;
6291    while (itr && (itr->text_node == n))
6292      {
6293         start -= itr->offset;
6294         end -= itr->offset;
6295         if ((end <= 0) && use_end)
6296           {
6297              break;
6298           }
6299         if (start <= 0)
6300           {
6301              return itr;
6302           }
6303         itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
6304      }
6305    return NULL;
6306 }
6307
6308 /**
6309  * Removes a text node and the corresponding format nodes.
6310  *
6311  * @param o the textblock objec.t
6312  * @param n the node to remove.
6313  */
6314 static void
6315 _evas_textblock_node_text_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *n)
6316 {
6317    _evas_textblock_node_text_adjust_offsets_to_start(o, n, 0, -1);
6318
6319    o->text_nodes = _NODE_TEXT(eina_inlist_remove(
6320            EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n)));
6321    _evas_textblock_node_text_free(n);
6322 }
6323
6324 /**
6325  * @internal
6326  * Return the position where the formats starts at.
6327  *
6328  * @param fmt the format to return the position of.
6329  * @return the position of the format in the text node it points to.
6330  */
6331 static size_t
6332 _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt)
6333 {
6334    Evas_Object_Textblock_Node_Text *text;
6335    Evas_Object_Textblock_Node_Format *base_format;
6336    Evas_Object_Textblock_Node_Format *itr;
6337    size_t position = 0;
6338
6339    if (!fmt) return 0;
6340    /* Find the main format node */
6341    text = fmt->text_node;
6342    base_format = text->format_node;
6343    EINA_INLIST_FOREACH(base_format, itr)
6344      {
6345         if (itr == fmt)
6346           {
6347              break;
6348           }
6349         position += itr->offset;
6350      }
6351    return position + fmt->offset;
6352 }
6353
6354 EAPI int
6355 evas_textblock_cursor_pos_get(const Evas_Textblock_Cursor *cur)
6356 {
6357    Evas_Object_Textblock *o;
6358    Evas_Object_Textblock_Node_Text *n;
6359    size_t npos = 0;
6360
6361    if (!cur) return -1;
6362    if (!cur->node) return 0;
6363    o = (Evas_Object_Textblock *)(cur->obj->object_data);
6364    n = o->text_nodes;
6365    while (n != cur->node)
6366      {
6367         npos += eina_ustrbuf_length_get(n->unicode);
6368         n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
6369      }
6370    return npos + cur->pos;
6371 }
6372
6373 EAPI void
6374 evas_textblock_cursor_pos_set(Evas_Textblock_Cursor *cur, int _pos)
6375 {
6376    Evas_Object_Textblock *o;
6377    Evas_Object_Textblock_Node_Text *n;
6378    size_t pos;
6379
6380    if (!cur) return;
6381    o = (Evas_Object_Textblock *)(cur->obj->object_data);
6382
6383    if (_pos < 0)
6384      {
6385         pos = 0;
6386      }
6387    else
6388      {
6389         pos = (size_t) _pos;
6390      }
6391
6392    n = o->text_nodes;
6393    while (n && (pos >= eina_ustrbuf_length_get(n->unicode)))
6394      {
6395         pos -= eina_ustrbuf_length_get(n->unicode);
6396         n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
6397      }
6398
6399    if (n)
6400      {
6401         cur->node = n;
6402         cur->pos = pos;
6403      }
6404    else if (o->text_nodes)
6405      {
6406         /* In case we went pass the last node, we need to put the cursor
6407          * at the absolute end. */
6408         Evas_Object_Textblock_Node_Text *last_n;
6409
6410         last_n = _NODE_TEXT(EINA_INLIST_GET(o->text_nodes)->last);
6411         pos = eina_ustrbuf_length_get(last_n->unicode);
6412
6413         cur->node = last_n;
6414         cur->pos = pos;
6415      }
6416
6417 }
6418
6419 EAPI Eina_Bool
6420 evas_textblock_cursor_line_set(Evas_Textblock_Cursor *cur, int line)
6421 {
6422    Evas_Object_Textblock *o;
6423    Evas_Object_Textblock_Line *ln;
6424    Evas_Object_Textblock_Item *it;
6425
6426    if (!cur) return EINA_FALSE;
6427    o = (Evas_Object_Textblock *)(cur->obj->object_data);
6428    if (!o->formatted.valid) _relayout(cur->obj);
6429
6430    ln = _find_layout_line_num(cur->obj, line);
6431    if (!ln) return EINA_FALSE;
6432    it = (Evas_Object_Textblock_Item *)ln->items;
6433    if (it)
6434      {
6435         cur->pos = it->text_pos;
6436         cur->node = it->text_node;
6437      }
6438    else
6439      {
6440         cur->pos = 0;
6441
6442         cur->node = o->text_nodes;
6443      }
6444    return EINA_TRUE;
6445 }
6446
6447 EAPI int
6448 evas_textblock_cursor_compare(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
6449 {
6450    Eina_Inlist *l1, *l2;
6451
6452    if (!cur1) return 0;
6453    if (!cur2) return 0;
6454    if (cur1->obj != cur2->obj) return 0;
6455    if ((!cur1->node) || (!cur2->node)) return 0;
6456    if (cur1->node == cur2->node)
6457      {
6458         if (cur1->pos < cur2->pos) return -1; /* cur1 < cur2 */
6459         else if (cur1->pos > cur2->pos) return 1; /* cur2 < cur1 */
6460         return 0;
6461      }
6462    for (l1 = EINA_INLIST_GET(cur1->node),
6463         l2 = EINA_INLIST_GET(cur1->node); (l1) || (l2);)
6464      {
6465         if (l1 == EINA_INLIST_GET(cur2->node)) return 1; /* cur2 < cur 1 */
6466         else if (l2 == EINA_INLIST_GET(cur2->node)) return -1; /* cur1 < cur 2 */
6467         else if (!l1) return -1; /* cur1 < cur 2 */
6468         else if (!l2) return 1; /* cur2 < cur 1 */
6469         l1 = l1->prev;
6470         l2 = l2->next;
6471      }
6472    return 0;
6473 }
6474
6475 EAPI void
6476 evas_textblock_cursor_copy(const Evas_Textblock_Cursor *cur, Evas_Textblock_Cursor *cur_dest)
6477 {
6478    if (!cur) return;
6479    if (!cur_dest) return;
6480    if (cur->obj != cur_dest->obj) return;
6481    cur_dest->pos = cur->pos;
6482    cur_dest->node = cur->node;
6483
6484 }
6485
6486
6487 /* text controls */
6488 /**
6489  * @internal
6490  * Free a text node. Shouldn't be used usually, it's better to use
6491  * @ref _evas_textblock_node_text_remove for most cases .
6492  *
6493  * @param n the text node to free
6494  * @see _evas_textblock_node_text_remove
6495  */
6496 static void
6497 _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n)
6498 {
6499    if (!n) return;
6500    eina_ustrbuf_free(n->unicode);
6501    if (n->utf8)
6502      free(n->utf8);
6503    if (n->par)
6504       n->par->text_node = NULL;
6505    free(n);
6506 }
6507
6508 /**
6509  * @internal
6510  * Create a new text node
6511  *
6512  * @return the new text node.
6513  */
6514 static Evas_Object_Textblock_Node_Text *
6515 _evas_textblock_node_text_new(void)
6516 {
6517    Evas_Object_Textblock_Node_Text *n;
6518
6519    n = calloc(1, sizeof(Evas_Object_Textblock_Node_Text));
6520    n->unicode = eina_ustrbuf_new();
6521    /* We want to layout each paragraph at least once. */
6522    n->dirty = EINA_TRUE;
6523    n->is_new = EINA_TRUE;
6524
6525    return n;
6526 }
6527
6528 /**
6529  * @internal
6530  * Break a paragraph. This does not add a PS but only splits the paragraph
6531  * where a ps was just added!
6532  *
6533  * @param cur the cursor to break at.
6534  * @param fnode the format node of the PS just added.
6535  * @return Returns no value.
6536  */
6537 static void
6538 _evas_textblock_cursor_break_paragraph(Evas_Textblock_Cursor *cur,
6539                               Evas_Object_Textblock_Node_Format *fnode)
6540 {
6541    Evas_Object_Textblock *o;
6542    Evas_Object_Textblock_Node_Text *n;
6543
6544    if (!cur) return;
6545    o = (Evas_Object_Textblock *)(cur->obj->object_data);
6546
6547    n = _evas_textblock_node_text_new();
6548    o->text_nodes = _NODE_TEXT(eina_inlist_append_relative(
6549                    EINA_INLIST_GET(o->text_nodes),
6550                    EINA_INLIST_GET(n),
6551                    EINA_INLIST_GET(cur->node)));
6552    /* Handle text and format changes. */
6553    if (cur->node)
6554      {
6555         Evas_Object_Textblock_Node_Format *nnode;
6556         size_t len, start;
6557         const Eina_Unicode *text;
6558
6559         /* If there was a format node in the delete range,
6560          * make it our format and update the text_node fields,
6561          * otherwise, use the paragraph separator
6562          * of the previous paragraph. */
6563         nnode  = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
6564         if (nnode && (nnode->text_node == cur->node))
6565           {
6566              n->format_node = nnode;
6567              nnode->offset--; /* We don't have to take the replacement char
6568                                  into account anymore */
6569              while (nnode && (nnode->text_node == cur->node))
6570                {
6571                   nnode->text_node = n;
6572                   nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
6573                }
6574           }
6575         else
6576           {
6577              n->format_node = fnode;
6578           }
6579
6580         /* cur->pos now points to the PS, move after. */
6581         start = cur->pos + 1;
6582         len = eina_ustrbuf_length_get(cur->node->unicode) - start;
6583         if (len > 0)
6584           {
6585              text = eina_ustrbuf_string_get(cur->node->unicode);
6586              eina_ustrbuf_append_length(n->unicode, text + start, len);
6587              eina_ustrbuf_remove(cur->node->unicode, start, start + len);
6588              cur->node->dirty = EINA_TRUE;
6589           }
6590      }
6591    else
6592      {
6593         fnode = o->format_nodes;
6594         if (fnode)
6595           {
6596              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->last);
6597           }
6598         n->format_node = fnode;
6599      }
6600 }
6601
6602 /**
6603  * @internal
6604  * Set the node and offset of all the curs after cur.
6605  *
6606  * @param cur the cursor.
6607  * @param n the current textblock node.
6608  * @param new_node the new node to set.
6609  */
6610 static void
6611 _evas_textblock_cursors_set_node(Evas_Object_Textblock *o,
6612       const Evas_Object_Textblock_Node_Text *n,
6613       Evas_Object_Textblock_Node_Text *new_node)
6614 {
6615    Eina_List *l;
6616    Evas_Textblock_Cursor *data;
6617
6618    if (n == o->cursor->node)
6619      {
6620         o->cursor->pos = 0;
6621         o->cursor->node = new_node;
6622      }
6623    EINA_LIST_FOREACH(o->cursors, l, data)
6624      {
6625         if (n == data->node)
6626           {
6627              data->pos = 0;
6628              data->node = new_node;
6629           }
6630      }
6631 }
6632
6633 /**
6634  * @internal
6635  * Update the offset of all the cursors after cur.
6636  *
6637  * @param cur the cursor.
6638  * @param n the current textblock node.
6639  * @param start the starting pos.
6640  * @param offset how much to adjust (can be negative).
6641  */
6642 static void
6643 _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor *cur,
6644       const Evas_Object_Textblock_Node_Text *n,
6645       size_t start, int offset)
6646 {
6647    Eina_List *l;
6648    Evas_Textblock_Cursor *data;
6649    Evas_Object_Textblock *o;
6650    o = (Evas_Object_Textblock *)(cur->obj->object_data);
6651
6652    if (cur != o->cursor)
6653      {
6654         if ((n == o->cursor->node) &&
6655               (o->cursor->pos > start))
6656           {
6657              if ((offset < 0) && (o->cursor->pos <= (size_t) (-1 * offset)))
6658                {
6659                   o->cursor->pos = 0;
6660                }
6661              else
6662                {
6663                   o->cursor->pos += offset;
6664                }
6665           }
6666      }
6667    EINA_LIST_FOREACH(o->cursors, l, data)
6668      {
6669         if (data != cur)
6670           {
6671              if ((n == data->node) &&
6672                    (data->pos > start))
6673                {
6674                   if ((offset < 0) && (data->pos <= (size_t) (-1 * offset)))
6675                     {
6676                        data->pos = 0;
6677                     }
6678                   else
6679                     {
6680                        data->pos += offset;
6681                     }
6682                }
6683              else if (!data->node)
6684                {
6685                   data->node = o->text_nodes;
6686                   data->pos = 0;
6687                }
6688           }
6689      }
6690 }
6691
6692 /**
6693  * @internal
6694  * Mark that the textblock has changed.
6695  *
6696  * @param o the textblock object.
6697  * @param obj the evas object.
6698  */
6699 static void
6700 _evas_textblock_changed(Evas_Object_Textblock *o, Evas_Object *obj)
6701 {
6702    o->formatted.valid = 0;
6703    o->native.valid = 0;
6704    o->content_changed = 1;
6705    if (o->markup_text)
6706      {
6707         free(o->markup_text);
6708         o->markup_text = NULL;
6709      }
6710
6711    evas_object_change(obj);
6712 }
6713
6714 static void
6715 _evas_textblock_invalidate_all(Evas_Object_Textblock *o)
6716 {
6717    Evas_Object_Textblock_Node_Text *n;
6718
6719    EINA_INLIST_FOREACH(o->text_nodes, n)
6720      {
6721         n->dirty = EINA_TRUE;
6722      }
6723 }
6724
6725 EAPI int
6726 evas_textblock_cursor_text_append(Evas_Textblock_Cursor *cur, const char *_text)
6727 {
6728    Evas_Object_Textblock *o;
6729    Evas_Object_Textblock_Node_Text *n;
6730    Evas_Object_Textblock_Node_Format *fnode = NULL;
6731    Eina_Unicode *text;
6732    int len = 0;
6733
6734    if (!cur) return 0;
6735    text = eina_unicode_utf8_to_unicode(_text, &len);
6736    o = (Evas_Object_Textblock *)(cur->obj->object_data);
6737
6738    n = cur->node;
6739    if (n)
6740      {
6741         Evas_Object_Textblock_Node_Format *nnode;
6742         fnode = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
6743         fnode = _evas_textblock_node_format_last_at_off(fnode);
6744         /* find the node after the current in the same paragraph
6745          * either we find one and then take the next, or we try to get
6746          * the first for the paragraph which must be after our position  */
6747         if (fnode)
6748           {
6749              if (!evas_textblock_cursor_format_is_visible_get(cur))
6750                {
6751                   nnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
6752                   if (nnode && (nnode->text_node == n))
6753                     {
6754                        fnode = nnode;
6755                     }
6756                   else
6757                     {
6758                        fnode = NULL;
6759                     }
6760                }
6761           }
6762         else
6763           {
6764              fnode = n->format_node;
6765           }
6766      }
6767    else if (o->text_nodes)
6768      {
6769         cur->node = o->text_nodes;
6770         cur->pos = 0;
6771      }
6772    else
6773      {
6774         n = _evas_textblock_node_text_new();
6775         o->text_nodes = _NODE_TEXT(eina_inlist_append(
6776                    EINA_INLIST_GET(o->text_nodes),
6777                    EINA_INLIST_GET(n)));
6778         cur->node = n;
6779      }
6780
6781    eina_ustrbuf_insert_length(n->unicode, text, len, cur->pos);
6782    /* Advance the formats */
6783    if (fnode && (fnode->text_node == cur->node))
6784      fnode->offset += len;
6785
6786    /* Update all the cursors after our position. */
6787    _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, len);
6788
6789    _evas_textblock_changed(o, cur->obj);
6790    n->dirty = EINA_TRUE;
6791    free(text);
6792
6793    if (!o->cursor->node)
6794       o->cursor->node = o->text_nodes;
6795    return len;
6796 }
6797
6798 EAPI int
6799 evas_textblock_cursor_text_prepend(Evas_Textblock_Cursor *cur, const char *_text)
6800 {
6801    int len;
6802    /*append is essentially prepend without advancing */
6803    len = evas_textblock_cursor_text_append(cur, _text);
6804    cur->pos += len; /*Advance */
6805    return len;
6806 }
6807
6808 /**
6809  * @internal
6810  * Free a format node
6811  *
6812  * @param o the textblock object
6813  * @param n the format node to free
6814  */
6815 static void
6816 _evas_textblock_node_format_free(Evas_Object_Textblock *o,
6817       Evas_Object_Textblock_Node_Format *n)
6818 {
6819    if (!n) return;
6820    eina_stringshare_del(n->format);
6821    eina_stringshare_del(n->orig_format);
6822    if (n->anchor == ANCHOR_ITEM)
6823       o->anchors_item = eina_list_remove(o->anchors_item, n);
6824    else if (n->anchor == ANCHOR_A)
6825       o->anchors_a = eina_list_remove(o->anchors_a, n);
6826    free(n);
6827 }
6828
6829 /**
6830  * @internal
6831  * Create a new format node.
6832  *
6833  * @param format the text to create the format node from.
6834  * @param o the textblock object.
6835  * @return Returns the new format node
6836  */
6837 static Evas_Object_Textblock_Node_Format *
6838 _evas_textblock_node_format_new(Evas_Object_Textblock *o, const char *_format)
6839 {
6840    Evas_Object_Textblock_Node_Format *n;
6841    const char *format = _format;
6842
6843    n = calloc(1, sizeof(Evas_Object_Textblock_Node_Format));
6844    /* Create orig_format and format */
6845    if (format[0] == '<')
6846      {
6847         const char *match;
6848         size_t format_len;
6849         size_t replace_len;
6850
6851         format++; /* Advance after '<' */
6852         format_len = strlen(format);
6853         if (format[format_len - 1] == '>')
6854            format_len--; /* We don't care about '>' */
6855
6856         match = _style_match_tag(o->style, format, format_len, &replace_len);
6857         if (match)
6858           {
6859              if ((match[0] == '+') || (match[0] == '-'))
6860                {
6861                   char *norm_format;
6862                   norm_format = malloc(format_len + 2 + 1);
6863                   memcpy(norm_format, match, 2);
6864                   memcpy(norm_format + 2, format, format_len);
6865                   norm_format[format_len + 2] = '\0';
6866                   n->orig_format =
6867                      eina_stringshare_add_length(norm_format, format_len + 2);
6868                   free(norm_format);
6869                }
6870              else
6871                {
6872                   n->orig_format =
6873                      eina_stringshare_add_length(format, format_len);
6874                }
6875              n->format = eina_stringshare_add(match);
6876           }
6877         else
6878           {
6879              char *norm_format;
6880
6881              norm_format = malloc(format_len + 2 + 1);
6882              if (norm_format)
6883                {
6884                   if (format[0] == '/')
6885                     {
6886                        memcpy(norm_format, "- ", 2);
6887                        memcpy(norm_format + 2, format + 1, format_len - 1);
6888                        norm_format[format_len + 2 - 1] = '\0';
6889                     }
6890                   else
6891                     {
6892                        memcpy(norm_format, "+ ", 2);
6893                        memcpy(norm_format + 2, format, format_len);
6894                        norm_format[format_len + 2] = '\0';
6895                     }
6896                   n->orig_format = eina_stringshare_add(norm_format);
6897                   free(norm_format);
6898                }
6899              n->format = eina_stringshare_ref(n->orig_format);
6900           }
6901      }
6902    /* Just use as is, it's a special format. */
6903    else
6904      {
6905         n->orig_format = eina_stringshare_add(format);
6906         n->format = eina_stringshare_ref(n->orig_format);
6907      }
6908
6909    format = n->format;
6910
6911    _evas_textblock_format_is_visible(n, format);
6912    if (n->anchor == ANCHOR_A)
6913      {
6914         o->anchors_a = eina_list_append(o->anchors_a, n);
6915      }
6916    else if (n->anchor == ANCHOR_ITEM)
6917      {
6918         o->anchors_item = eina_list_append(o->anchors_item, n);
6919      }
6920    n->is_new = EINA_TRUE;
6921
6922    return n;
6923 }
6924
6925 static Eina_Bool
6926 _evas_textblock_cursor_is_at_the_end(const Evas_Textblock_Cursor *cur)
6927 {
6928    const Eina_Unicode *text;
6929
6930    if (!cur) return EINA_FALSE;
6931    if (!cur->node) return EINA_FALSE;
6932    text = eina_ustrbuf_string_get(cur->node->unicode);
6933    return ((text[cur->pos] == 0) && (!EINA_INLIST_GET(cur->node)->next)) ?
6934               EINA_TRUE : EINA_FALSE;
6935 }
6936
6937 EAPI Eina_Bool
6938 evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, const char *format)
6939 {
6940    Evas_Object_Textblock *o;
6941    Evas_Object_Textblock_Node_Format *n;
6942    Eina_Bool is_visible;
6943
6944    if (!cur) return EINA_FALSE;
6945    if ((!format) || (format[0] == 0)) return EINA_FALSE;
6946    o = (Evas_Object_Textblock *)(cur->obj->object_data);
6947    /* We should always have at least one text node */
6948    if (!o->text_nodes)
6949      {
6950         evas_textblock_cursor_text_prepend(cur, "");
6951      }
6952
6953    n = _evas_textblock_node_format_new(o, format);
6954    is_visible = n->visible;
6955    format = n->format;
6956    if (!cur->node)
6957      {
6958         o->format_nodes = _NODE_FORMAT(eina_inlist_append(
6959                  EINA_INLIST_GET(o->format_nodes),
6960                  EINA_INLIST_GET(n)));
6961         cur->pos = 0;
6962         n->text_node = (EINA_INLIST_GET(n)->prev) ?
6963            _NODE_FORMAT(EINA_INLIST_GET(n)->prev)->text_node :
6964            o->text_nodes;
6965         cur->node = n->text_node;
6966      }
6967    else
6968      {
6969         Evas_Object_Textblock_Node_Format *fmt;
6970         fmt = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
6971         n->text_node = cur->node;
6972         if (!fmt)
6973           {
6974              o->format_nodes = _NODE_FORMAT(eina_inlist_prepend(
6975                       EINA_INLIST_GET(o->format_nodes),
6976                       EINA_INLIST_GET(n)));
6977              n->offset = cur->pos;
6978           }
6979         else
6980           {
6981              if (evas_textblock_cursor_format_is_visible_get(cur))
6982                {
6983                   o->format_nodes = _NODE_FORMAT(eina_inlist_prepend_relative(
6984                            EINA_INLIST_GET(o->format_nodes),
6985                            EINA_INLIST_GET(n),
6986                            EINA_INLIST_GET(fmt)
6987                            ));
6988                   n->offset = fmt->offset;
6989                   if (fmt->text_node->format_node == fmt)
6990                     {
6991                        fmt->text_node->format_node = n;
6992                     }
6993                }
6994              else
6995                {
6996                   fmt = _evas_textblock_node_format_last_at_off(fmt);
6997                   o->format_nodes = _NODE_FORMAT(eina_inlist_append_relative(
6998                            EINA_INLIST_GET(o->format_nodes),
6999                            EINA_INLIST_GET(n),
7000                            EINA_INLIST_GET(fmt)
7001                            ));
7002                   if (fmt->text_node != cur->node)
7003                     {
7004                        n->offset = cur->pos;
7005                     }
7006                   else
7007                     {
7008                        n->offset = cur->pos -
7009                           _evas_textblock_node_format_pos_get(fmt);
7010                     }
7011                }
7012           }
7013         /* Adjust differently if we insert a format char */
7014         if (is_visible)
7015           {
7016              _evas_textblock_node_format_adjust_offset(o, cur->node, n,
7017                    -(n->offset - 1));
7018           }
7019         else
7020           {
7021              _evas_textblock_node_format_adjust_offset(o, cur->node, n,
7022                    -n->offset);
7023           }
7024
7025         if (!fmt || (fmt->text_node != cur->node))
7026           {
7027              cur->node->format_node = n;
7028           }
7029      }
7030    if (is_visible && cur->node)
7031      {
7032         Eina_Unicode insert_char;
7033         /* Insert a visual representation according to the type of the
7034            format */
7035         if (_IS_PARAGRAPH_SEPARATOR(o, format))
7036            insert_char = _PARAGRAPH_SEPARATOR;
7037         else if (_IS_LINE_SEPARATOR(format))
7038            insert_char = '\n';
7039         else if (_IS_TAB(format))
7040            insert_char = '\t';
7041         else
7042            insert_char = EVAS_TEXTBLOCK_REPLACEMENT_CHAR;
7043
7044         eina_ustrbuf_insert_char(cur->node->unicode, insert_char, cur->pos);
7045
7046         /* Advance all the cursors after our cursor */
7047         _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, 1);
7048         if (_IS_PARAGRAPH_SEPARATOR(o, format))
7049           {
7050              _evas_textblock_cursor_break_paragraph(cur, n);
7051           }
7052         else
7053           {
7054              /* Handle visible format nodes here */
7055              cur->node->dirty = EINA_TRUE;
7056              n->is_new = EINA_FALSE;
7057           }
7058      }
7059    else
7060      {
7061         o->format_changed = EINA_TRUE;
7062      }
7063
7064    _evas_textblock_changed(o, cur->obj);
7065
7066    if (!o->cursor->node)
7067       o->cursor->node = o->text_nodes;
7068    return is_visible;
7069 }
7070
7071 EAPI Eina_Bool
7072 evas_textblock_cursor_format_prepend(Evas_Textblock_Cursor *cur, const char *format)
7073 {
7074    Eina_Bool is_visible;
7075    /* append is essentially prepend without advancing */
7076    is_visible = evas_textblock_cursor_format_append(cur, format);
7077    if (is_visible)
7078      {
7079         /* Advance after the replacement char */
7080         evas_textblock_cursor_char_next(cur);
7081      }
7082
7083    return is_visible;
7084 }
7085
7086
7087 EAPI void
7088 evas_textblock_cursor_char_delete(Evas_Textblock_Cursor *cur)
7089 {
7090    Evas_Object_Textblock *o;
7091    Evas_Object_Textblock_Node_Text *n, *n2;
7092    const Eina_Unicode *text;
7093    int chr, ind, ppos;
7094
7095    if (!cur || !cur->node) return;
7096    o = (Evas_Object_Textblock *)(cur->obj->object_data);
7097    n = cur->node;
7098
7099    text = eina_ustrbuf_string_get(n->unicode);
7100    ind = cur->pos;
7101    if (text[ind])
7102       chr = text[ind++];
7103    else
7104       chr = 0;
7105
7106    if (chr == 0) return;
7107    ppos = cur->pos;
7108    eina_ustrbuf_remove(n->unicode, cur->pos, ind);
7109    /* Remove a format node if needed, and remove the char only if the
7110     * fmt node is not visible */
7111      {
7112         Eina_Bool should_merge = EINA_FALSE;
7113         Evas_Object_Textblock_Node_Format *fmt, *fmt2;
7114         fmt = _evas_textblock_cursor_node_format_at_pos_get(cur);
7115         if (fmt)
7116           {
7117              const char *format = NULL;
7118              Evas_Object_Textblock_Node_Format *last_fmt;
7119              /* If there's a PS it must be the last become it delimits paragraphs */
7120              last_fmt = _evas_textblock_node_format_last_at_off(fmt);
7121              format = last_fmt->format;
7122              if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
7123                {
7124                   /* If it was a paragraph separator, we should merge the
7125                    * current with the next, there must be a next. */
7126                   should_merge = EINA_TRUE;
7127                }
7128              /* If a singnular, mark as invisible, so we'll delete it. */
7129              if (!format || ((*format != '+') && (*format != '-')))
7130                {
7131                   last_fmt->visible = EINA_FALSE;
7132                }
7133           }
7134
7135         fmt2 = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
7136         fmt2 = _evas_textblock_node_format_last_at_off(fmt2);
7137         _evas_textblock_node_format_adjust_offset(o, cur->node, fmt2,
7138               -(ind - cur->pos));
7139
7140         if (should_merge)
7141           {
7142              _evas_textblock_cursor_nodes_merge(cur);
7143           }
7144
7145         _evas_textblock_node_format_remove_matching(o, fmt);
7146      }
7147
7148    if (cur->pos == eina_ustrbuf_length_get(n->unicode))
7149      {
7150         n2 = _NODE_TEXT(EINA_INLIST_GET(n)->next);
7151         if (n2)
7152           {
7153              cur->node = n2;
7154              cur->pos = 0;
7155           }
7156      }
7157
7158    _evas_textblock_cursors_update_offset(cur, n, ppos, -(ind - ppos));
7159    _evas_textblock_changed(o, cur->obj);
7160    cur->node->dirty = EINA_TRUE;
7161 }
7162
7163 EAPI void
7164 evas_textblock_cursor_range_delete(Evas_Textblock_Cursor *cur1, Evas_Textblock_Cursor *cur2)
7165 {
7166    Evas_Object_Textblock_Node_Format *fnode = NULL;
7167    Evas_Object_Textblock *o;
7168    Evas_Object_Textblock_Node_Text *n1, *n2;
7169    Eina_Bool should_merge = EINA_FALSE, reset_cursor = EINA_FALSE;
7170
7171    if (!cur1 || !cur1->node) return;
7172    if (!cur2 || !cur2->node) return;
7173    if (cur1->obj != cur2->obj) return;
7174    o = (Evas_Object_Textblock *)(cur1->obj->object_data);
7175    if (evas_textblock_cursor_compare(cur1, cur2) > 0)
7176      {
7177         Evas_Textblock_Cursor *tc;
7178
7179         tc = cur1;
7180         cur1 = cur2;
7181         cur2 = tc;
7182      }
7183    n1 = cur1->node;
7184    n2 = cur2->node;
7185    if ((evas_textblock_cursor_compare(o->cursor, cur1) >= 0) &&
7186          (evas_textblock_cursor_compare(cur2, o->cursor) >= 0))
7187      {
7188         reset_cursor = EINA_TRUE;
7189      }
7190
7191
7192    if (n1 == n2)
7193      {
7194         if ((cur1->pos == 0) &&
7195               (cur2->pos == eina_ustrbuf_length_get(n1->unicode)))
7196           {
7197              _evas_textblock_node_text_remove_formats_between(o, n1, 0, -1);
7198           }
7199         else
7200           {
7201              should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o,
7202                    n1, cur1->pos, cur2->pos);
7203           }
7204         eina_ustrbuf_remove(n1->unicode, cur1->pos, cur2->pos);
7205         _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos, - (cur2->pos - cur1->pos));
7206      }
7207    else
7208      {
7209         Evas_Object_Textblock_Node_Text *n;
7210         int len;
7211         _evas_textblock_node_text_adjust_offsets_to_start(o, n1, cur1->pos, -1);
7212         n = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7213         /* Remove all the text nodes between */
7214         while (n && (n != n2))
7215           {
7216              Evas_Object_Textblock_Node_Text *nnode;
7217
7218              nnode = _NODE_TEXT(EINA_INLIST_GET(n)->next);
7219              _evas_textblock_cursors_set_node(o, n, n1);
7220              _evas_textblock_node_text_remove(o, n);
7221              n = nnode;
7222           }
7223         should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o, n2,
7224               0, cur2->pos);
7225
7226         /* Remove the formats and the strings in the first and last nodes */
7227         len = eina_ustrbuf_length_get(n1->unicode);
7228         eina_ustrbuf_remove(n1->unicode, cur1->pos, len);
7229         eina_ustrbuf_remove(n2->unicode, 0, cur2->pos);
7230         /* Merge the nodes because we removed the PS */
7231         _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos,
7232               - cur1->pos);
7233         _evas_textblock_cursors_update_offset(cur2, cur2->node, 0, - cur2->pos);
7234         _evas_textblock_nodes_merge(o, n1);
7235      }
7236    fnode = _evas_textblock_cursor_node_format_at_pos_get(cur1);
7237
7238    if (should_merge)
7239      {
7240         /* We call this function instead of the cursor one because we already
7241          * updated the cursors */
7242         _evas_textblock_nodes_merge(o, n1);
7243      }
7244    _evas_textblock_node_format_remove_matching(o, fnode);
7245
7246    evas_textblock_cursor_copy(cur1, cur2);
7247    if (reset_cursor)
7248      evas_textblock_cursor_copy(cur1, o->cursor);
7249
7250    _evas_textblock_changed(o, cur1->obj);
7251    n1->dirty = n2->dirty = EINA_TRUE;
7252 }
7253
7254
7255 EAPI char *
7256 evas_textblock_cursor_content_get(const Evas_Textblock_Cursor *cur)
7257 {
7258    const Eina_Unicode *ustr;
7259    Eina_Unicode buf[2];
7260    char *s;
7261    if (!cur || !cur->node) return NULL;
7262    if (evas_textblock_cursor_format_is_visible_get(cur))
7263      {
7264         size_t len;
7265         const char *fstr;
7266         char *ret;
7267         int pop = 0;
7268         fstr  = evas_textblock_node_format_text_get(
7269               _evas_textblock_node_visible_at_pos_get(
7270                  evas_textblock_cursor_format_get(cur)));
7271
7272         if (!fstr)
7273            return NULL;
7274
7275         if (*fstr == '-') pop = 1;
7276         while ((*fstr == ' ') || (*fstr == '+') || (*fstr == '-')) fstr++;
7277         len = strlen(fstr);
7278
7279           {
7280              char *tmp;
7281              if (pop)
7282                {
7283                   ret = tmp = malloc(len + 3 + 1); /* </> and the null */
7284                   memcpy(tmp, "</", 2);
7285                   tmp += 2;
7286                }
7287              else
7288                {
7289                   ret = tmp = malloc(len + 2 + 1); /* <> and the null */
7290                   *tmp = '<';
7291                   tmp++;
7292                }
7293              memcpy(tmp, fstr, len);
7294              memcpy(tmp + len, ">", 2); /* Including the null */
7295           }
7296
7297         return ret;
7298      }
7299
7300    ustr = eina_ustrbuf_string_get(cur->node->unicode);
7301    buf[0] = ustr[cur->pos];
7302    buf[1] = 0;
7303    s = eina_unicode_unicode_to_utf8(buf, NULL);
7304
7305    return s;
7306 }
7307
7308 static char *
7309 _evas_textblock_cursor_range_text_markup_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2)
7310 {
7311    Evas_Object_Textblock *o;
7312    Evas_Object_Textblock_Node_Text *tnode;
7313    Eina_Strbuf *buf;
7314    Evas_Textblock_Cursor *cur2;
7315    buf = eina_strbuf_new();
7316
7317    if (!cur1 || !cur1->node) return NULL;
7318    if (!_cur2 || !_cur2->node) return NULL;
7319    if (cur1->obj != _cur2->obj) return NULL;
7320    o = (Evas_Object_Textblock *)(cur1->obj->object_data);
7321    if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
7322      {
7323         const Evas_Textblock_Cursor *tc;
7324
7325         tc = cur1;
7326         cur1 = _cur2;
7327         _cur2 = tc;
7328      }
7329    /* Work on a local copy of the cur */
7330    cur2 = alloca(sizeof(Evas_Textblock_Cursor));
7331    cur2->obj = _cur2->obj;
7332    evas_textblock_cursor_copy(_cur2, cur2);
7333
7334    /* Parse the text between the cursors. */
7335    for (tnode = cur1->node ; tnode ;
7336          tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next))
7337      {
7338         Evas_Object_Textblock_Node_Format *fnode;
7339         Eina_Unicode *text_base, *text;
7340         int off = 0;
7341
7342         text_base = text =
7343            eina_unicode_strndup(eina_ustrbuf_string_get(tnode->unicode),
7344                                 eina_ustrbuf_length_get(tnode->unicode));
7345         if (tnode == cur2->node)
7346           {
7347              fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7348                    cur1->pos, cur2->pos);
7349           }
7350         else if (tnode == cur1->node)
7351           {
7352              fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7353                    cur1->pos, -1);
7354           }
7355         else
7356           {
7357              fnode = _evas_textblock_node_text_get_first_format_between(tnode,
7358                    0, -1);
7359           }
7360         /* Init the offset so the first one will count starting from cur1->pos
7361          * and not the previous format node */
7362         if (tnode == cur1->node)
7363           {
7364              if (fnode)
7365                {
7366                   off = _evas_textblock_node_format_pos_get(fnode) -
7367                      cur1->pos - fnode->offset;
7368                }
7369              text += cur1->pos;
7370           }
7371         else
7372           {
7373              off = 0;
7374           }
7375         while (fnode && (fnode->text_node == tnode))
7376           {
7377              Eina_Unicode tmp_ch;
7378              off += fnode->offset;
7379              if ((tnode == cur2->node) &&
7380                    ((size_t) (text - text_base + off) >= cur2->pos))
7381                {
7382                   break;
7383                }
7384              /* No need to skip on the first run */
7385              tmp_ch = text[off];
7386              text[off] = 0; /* Null terminate the part of the string */
7387              _markup_get_text_append(buf, text);
7388              _markup_get_format_append(o, buf, fnode);
7389              text[off] = tmp_ch; /* Restore the char */
7390              text += off;
7391              if (fnode->visible)
7392                {
7393                   off = -1;
7394                   text++;
7395                }
7396              else
7397                {
7398                   off = 0;
7399                }
7400              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7401           }
7402         /* If we got to the last node, stop and add the rest outside */
7403         if (cur2->node == tnode)
7404           {
7405              /* Add the rest, skip replacement */
7406              /* Don't go past the second cursor pos */
7407              text_base[cur2->pos] = '\0';
7408              _markup_get_text_append(buf, text);
7409              free(text_base);
7410              break;
7411           }
7412         else
7413           {
7414              /* Add the rest, skip replacement */
7415              _markup_get_text_append(buf, text);
7416              free(text_base);
7417           }
7418      }
7419    /* return the string */
7420      {
7421         char *ret;
7422         ret = eina_strbuf_string_steal(buf);
7423         eina_strbuf_free(buf);
7424         return ret;
7425      }
7426 }
7427
7428 static char *
7429 _evas_textblock_cursor_range_text_plain_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2)
7430 {
7431    Eina_UStrbuf *buf;
7432    Evas_Object_Textblock_Node_Text *n1, *n2;
7433    Evas_Textblock_Cursor *cur2;
7434
7435    buf = eina_ustrbuf_new();
7436
7437    if (!cur1 || !cur1->node) return NULL;
7438    if (!_cur2 || !_cur2->node) return NULL;
7439    if (cur1->obj != _cur2->obj) return NULL;
7440    if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
7441      {
7442         const Evas_Textblock_Cursor *tc;
7443
7444         tc = cur1;
7445         cur1 = _cur2;
7446         _cur2 = tc;
7447      }
7448    n1 = cur1->node;
7449    n2 = _cur2->node;
7450    /* Work on a local copy of the cur */
7451    cur2 = alloca(sizeof(Evas_Textblock_Cursor));
7452    cur2->obj = _cur2->obj;
7453    evas_textblock_cursor_copy(_cur2, cur2);
7454
7455
7456    if (n1 == n2)
7457      {
7458         const Eina_Unicode *tmp;
7459         tmp = eina_ustrbuf_string_get(n1->unicode);
7460         eina_ustrbuf_append_length(buf, tmp + cur1->pos, cur2->pos - cur1->pos);
7461      }
7462    else
7463      {
7464         const Eina_Unicode *tmp;
7465         tmp = eina_ustrbuf_string_get(n1->unicode);
7466         eina_ustrbuf_append(buf, tmp + cur1->pos);
7467         n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7468         while (n1 != n2)
7469           {
7470              tmp = eina_ustrbuf_string_get(n1->unicode);
7471              eina_ustrbuf_append_length(buf, tmp,
7472                    eina_ustrbuf_length_get(n1->unicode));
7473              n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
7474           }
7475         tmp = eina_ustrbuf_string_get(n2->unicode);
7476         eina_ustrbuf_append_length(buf, tmp, cur2->pos);
7477      }
7478
7479    /* Free and return */
7480      {
7481         char *ret;
7482         ret = eina_unicode_unicode_to_utf8(eina_ustrbuf_string_get(buf), NULL);
7483         eina_ustrbuf_free(buf);
7484         return ret;
7485      }
7486 }
7487
7488 EAPI Eina_List *
7489 evas_textblock_cursor_range_formats_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
7490 {
7491    Evas_Object *obj = cur1->obj;
7492    Eina_List *ret = NULL;
7493    Evas_Object_Textblock_Node_Text *n1, *n2;
7494    Evas_Object_Textblock_Node_Format *first, *last;
7495    TB_HEAD_RETURN(NULL);
7496    if (!cur1 || !cur1->node) return NULL;
7497    if (!cur2 || !cur2->node) return NULL;
7498    if (cur1->obj != cur2->obj) return NULL;
7499    if (evas_textblock_cursor_compare(cur1, cur2) > 0)
7500      {
7501         const Evas_Textblock_Cursor *tc;
7502
7503         tc = cur1;
7504         cur1 = cur2;
7505         cur2 = tc;
7506      }
7507    n1 = cur1->node;
7508    n2 = cur2->node;
7509
7510    /* FIXME: Change first and last getting to format_before_or_at_pos_get */
7511
7512    last = n2->format_node;
7513
7514    /* If n2->format_node is NULL, we don't have formats in the tb/range. */
7515    if (!last)
7516       return NULL;
7517    /* If the found format is on our text node, we should go to the last
7518     * one, otherwise, the one we found is good enough. */
7519    if (last->text_node == n2)
7520      {
7521         Evas_Object_Textblock_Node_Format *fnode = last;
7522         while (fnode && (fnode->text_node == n2))
7523           {
7524              last = fnode;
7525              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7526           }
7527      }
7528
7529    /* If the first format node is within the range (i.e points to n1) or if
7530     * we have other formats in the range, go through them */
7531    first = n1->format_node;
7532    if ((first->text_node == n1) || (first != last))
7533      {
7534         Evas_Object_Textblock_Node_Format *fnode = first;
7535         /* Go to the first one in the range */
7536         if (first->text_node != n1)
7537           {
7538              first = _NODE_FORMAT(EINA_INLIST_GET(first)->next);
7539           }
7540
7541         while (fnode)
7542           {
7543              ret = eina_list_append(ret, fnode);
7544              if (fnode == last)
7545                 break;
7546              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7547           }
7548      }
7549
7550    return ret;
7551
7552 }
7553
7554 EAPI char *
7555 evas_textblock_cursor_range_text_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2, Evas_Textblock_Text_Type format)
7556 {
7557    if (format == EVAS_TEXTBLOCK_TEXT_MARKUP)
7558       return _evas_textblock_cursor_range_text_markup_get(cur1, cur2);
7559    else if (format == EVAS_TEXTBLOCK_TEXT_PLAIN)
7560       return _evas_textblock_cursor_range_text_plain_get(cur1, cur2);
7561    else
7562       return NULL; /* Not yet supported */
7563 }
7564
7565 EAPI const char *
7566 evas_textblock_cursor_paragraph_text_get(const Evas_Textblock_Cursor *cur)
7567 {
7568    Evas_Textblock_Cursor cur1, cur2;
7569    if (!cur) return NULL;
7570    if (!cur->node) return NULL;
7571    if (cur->node->utf8)
7572      {
7573         free(cur->node->utf8);
7574      }
7575    cur1.obj = cur2.obj = cur->obj;
7576    cur1.node = cur2.node = cur->node;
7577    evas_textblock_cursor_paragraph_char_first(&cur1);
7578    evas_textblock_cursor_paragraph_char_last(&cur2);
7579
7580    cur->node->utf8 = evas_textblock_cursor_range_text_get(&cur1, &cur2,
7581          EVAS_TEXTBLOCK_TEXT_MARKUP);
7582    return cur->node->utf8;
7583 }
7584
7585 EAPI int
7586 evas_textblock_cursor_paragraph_text_length_get(const Evas_Textblock_Cursor *cur)
7587 {
7588    int len;
7589    if (!cur) return -1;
7590    if (!cur->node) return -1;
7591    len = eina_ustrbuf_length_get(cur->node->unicode);
7592
7593    if (EINA_INLIST_GET(cur->node)->next)
7594       return len - 1; /* Remove the paragraph separator */
7595    else
7596       return len;
7597 }
7598
7599 EAPI const Evas_Object_Textblock_Node_Format *
7600 evas_textblock_cursor_format_get(const Evas_Textblock_Cursor *cur)
7601 {
7602    if (!cur) return NULL;
7603    if (!cur->node) return NULL;
7604    return _evas_textblock_cursor_node_format_at_pos_get(cur);
7605 }
7606
7607 EAPI const char *
7608 evas_textblock_node_format_text_get(const Evas_Object_Textblock_Node_Format *fmt)
7609 {
7610    if (!fmt) return NULL;
7611    return fmt->orig_format;
7612 }
7613
7614 EAPI void
7615 evas_textblock_cursor_at_format_set(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *fmt)
7616 {
7617    if (!fmt || !cur) return;
7618    cur->node = fmt->text_node;
7619    cur->pos = _evas_textblock_node_format_pos_get(fmt);
7620 }
7621
7622 EAPI Eina_Bool
7623 evas_textblock_cursor_format_is_visible_get(const Evas_Textblock_Cursor *cur)
7624 {
7625    const Eina_Unicode *text;
7626
7627    if (!cur) return EINA_FALSE;
7628    if (!cur->node) return EINA_FALSE;
7629    text = eina_ustrbuf_string_get(cur->node->unicode);
7630    return EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(text[cur->pos]);
7631 }
7632
7633 EAPI int
7634 evas_textblock_cursor_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch, Evas_BiDi_Direction *dir, Evas_Textblock_Cursor_Type ctype)
7635 {
7636    int ret = -1;
7637    const Evas_Textblock_Cursor *dir_cur;
7638    Evas_Textblock_Cursor cur2;
7639    Evas_Object_Textblock *o;
7640    o = (Evas_Object_Textblock *)(cur->obj->object_data);
7641    if (!o->formatted.valid) _relayout(cur->obj);
7642
7643    dir_cur = cur;
7644    if (ctype == EVAS_TEXTBLOCK_CURSOR_UNDER)
7645      {
7646         ret = evas_textblock_cursor_pen_geometry_get(cur, cx, cy, cw, ch);
7647      }
7648    else if (ctype == EVAS_TEXTBLOCK_CURSOR_BEFORE)
7649      {
7650         /* In the case of a "before cursor", we should get the coordinates
7651          * of just after the previous char (which in bidi text may not be
7652          * just before the current char). */
7653         Evas_Coord x, y, h, w;
7654         Evas_Object_Textblock_Node_Format *fmt;
7655
7656         /* If it's at the end of the line, we want to get the position, not
7657          * the position of the previous */
7658         if ((cur->pos > 0) && !_evas_textblock_cursor_is_at_the_end(cur))
7659           {
7660              Eina_Bool before_char = EINA_FALSE;
7661              cur2.obj = cur->obj;
7662              evas_textblock_cursor_copy(cur, &cur2);
7663              evas_textblock_cursor_char_prev(&cur2);
7664
7665              fmt = _evas_textblock_cursor_node_format_at_pos_get(&cur2);
7666
7667              if (!fmt || !_IS_LINE_SEPARATOR(fmt->format))
7668                {
7669                   dir_cur = &cur2;
7670                   before_char = EINA_FALSE;
7671                }
7672              else
7673                {
7674                   before_char = EINA_TRUE;
7675                }
7676              ret = evas_textblock_cursor_pen_geometry_get(
7677                    dir_cur, &x, &y, &w, &h);
7678 #ifdef BIDI_SUPPORT
7679              /* Adjust if the char is an rtl char */
7680              if (ret >= 0)
7681                {
7682                   Eina_Bool is_rtl = EINA_FALSE;
7683                   if (dir_cur->node->par->is_bidi)
7684                     {
7685                        Evas_Object_Textblock_Line *ln;
7686                        Evas_Object_Textblock_Item *it;
7687                        _find_layout_item_match(dir_cur, &ln, &it);
7688                        if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
7689                              (_ITEM_TEXT(it)->text_props.bidi.dir ==
7690                               EVAS_BIDI_DIRECTION_RTL))
7691                           is_rtl = EINA_TRUE;
7692                        else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
7693                              (_ITEM_FORMAT(it)->bidi_dir ==
7694                               EVAS_BIDI_DIRECTION_RTL))
7695                           is_rtl = EINA_TRUE;
7696                     }
7697
7698                   if ((!before_char && is_rtl) ||
7699                         (before_char && !is_rtl))
7700                     {
7701                        /* Just don't advance the width */
7702                        w = 0;
7703                     }
7704                }
7705 #endif
7706           }
7707         else if (cur->pos == 0)
7708           {
7709              ret = evas_textblock_cursor_pen_geometry_get(
7710                    dir_cur, &x, &y, &w, &h);
7711 #ifdef BIDI_SUPPORT
7712              Eina_Bool is_rtl = EINA_FALSE;
7713              if (dir_cur->node->par->is_bidi)
7714                {
7715                   Evas_Object_Textblock_Line *ln;
7716                   Evas_Object_Textblock_Item *it;
7717                   _find_layout_item_match(dir_cur, &ln, &it);
7718                   if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
7719                         (_ITEM_TEXT(it)->text_props.bidi.dir ==
7720                          EVAS_BIDI_DIRECTION_RTL))
7721                      is_rtl = EINA_TRUE;
7722                   else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
7723                         (_ITEM_FORMAT(it)->bidi_dir ==
7724                          EVAS_BIDI_DIRECTION_RTL))
7725                      is_rtl = EINA_TRUE;
7726                }
7727
7728              /* Adjust if the char is an rtl char */
7729              if ((ret >= 0) && (!is_rtl))
7730                {
7731                   /* Just don't advance the width */
7732                   w = 0;
7733                }
7734 #endif
7735           }
7736         else
7737           {
7738              ret = evas_textblock_cursor_pen_geometry_get(
7739                    dir_cur, &x, &y, &w, &h);
7740           }
7741         if (ret >= 0)
7742           {
7743              if (cx) *cx = x + w;
7744              if (cy) *cy = y;
7745              if (cw) *cw = 0;
7746              if (ch) *ch = h;
7747           }
7748      }
7749
7750    if (dir && dir_cur && dir_cur->node)
7751      {
7752 #ifdef BIDI_SUPPORT
7753         Eina_Bool is_rtl = EINA_FALSE;
7754         if (dir_cur->node->par->is_bidi)
7755           {
7756              Evas_Object_Textblock_Line *ln;
7757              Evas_Object_Textblock_Item *it;
7758              _find_layout_item_match(dir_cur, &ln, &it);
7759              if ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
7760                    (_ITEM_TEXT(it)->text_props.bidi.dir ==
7761                     EVAS_BIDI_DIRECTION_RTL))
7762                 is_rtl = EINA_TRUE;
7763              else if ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
7764                    (_ITEM_FORMAT(it)->bidi_dir ==
7765                     EVAS_BIDI_DIRECTION_RTL))
7766                 is_rtl = EINA_TRUE;
7767           }
7768
7769         if (_evas_textblock_cursor_is_at_the_end(dir_cur) && (dir_cur->pos > 0))
7770           {
7771              *dir = (is_rtl) ?
7772                 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
7773           }
7774         else if (dir_cur->pos > 0)
7775           {
7776              *dir = (is_rtl) ?
7777                 EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
7778           }
7779         else
7780 #endif
7781           {
7782              *dir = EVAS_BIDI_DIRECTION_LTR;
7783           }
7784      }
7785    return ret;
7786 }
7787
7788 /**
7789  * @internal
7790  * Returns the geometry/pen position (depending on query_func) of the char
7791  * at pos.
7792  *
7793  * @param cur the position of the char.
7794  * @param query_func the query function to use.
7795  * @param cx the x of the char (or pen_x in the case of pen position).
7796  * @param cy the y of the char.
7797  * @param cw the w of the char (or advance in the case pen position).
7798  * @param ch the h of the char.
7799  * @return line number of the char on success, -1 on error.
7800  */
7801 static int
7802 _evas_textblock_cursor_char_pen_geometry_common_get(int (*query_func) (void *data, Evas_Font_Set *font, const Evas_Text_Props *intl_props, int pos, int *cx, int *cy, int *cw, int *ch), const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
7803 {
7804    Evas_Object_Textblock *o;
7805    Evas_Object_Textblock_Line *ln = NULL;
7806    Evas_Object_Textblock_Item *it = NULL;
7807    Evas_Object_Textblock_Text_Item *ti = NULL;
7808    Evas_Object_Textblock_Format_Item *fi = NULL;
7809    int x = 0, y = 0, w = 0, h = 0;
7810    int pos;
7811    Eina_Bool previous_format;
7812
7813    if (!cur) return -1;
7814    o = (Evas_Object_Textblock *)(cur->obj->object_data);
7815    if (!o->formatted.valid) _relayout(cur->obj);
7816
7817    if (!cur->node)
7818      {
7819         if (!o->text_nodes)
7820           {
7821              if (!o->paragraphs) return -1;
7822              ln = o->paragraphs->lines;
7823              if (!ln) return -1;
7824              if (cx) *cx = ln->x;
7825              if (cy) *cy = ln->par->y + ln->y;
7826              if (cw) *cw = ln->w;
7827              if (ch) *ch = ln->h;
7828              return ln->par->line_no + ln->line_no;
7829           }
7830         else
7831           return -1;
7832      }
7833
7834    previous_format = _find_layout_item_match(cur, &ln, &it);
7835    if (!it)
7836      {
7837         return -1;
7838      }
7839    if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
7840      {
7841         ti = _ITEM_TEXT(it);
7842      }
7843    else
7844      {
7845         fi = _ITEM_FORMAT(it);
7846      }
7847
7848    if (ln && ti)
7849      {
7850         pos = cur->pos - ti->parent.text_pos;
7851
7852         if (pos < 0) pos = 0;
7853         if (ti->parent.format->font.font)
7854           {
7855              query_func(cur->ENDT,
7856                    ti->parent.format->font.font,
7857                    &ti->text_props,
7858                    pos,
7859                    &x, &y, &w, &h);
7860           }
7861
7862         x += ln->x + _ITEM(ti)->x;
7863
7864         if (x < ln->x)
7865           {
7866              x = ln->x;
7867           }
7868         y = ln->par->y + ln->y;
7869         h = ln->h;
7870      }
7871    else if (ln && fi)
7872      {
7873         if (previous_format)
7874           {
7875              if (_IS_LINE_SEPARATOR(fi->item))
7876                {
7877                   x = 0;
7878                   y = ln->par->y + ln->y + ln->h;
7879                }
7880              else
7881                {
7882 #ifdef BIDI_SUPPORT
7883                   if (ln->par->direction == EVAS_BIDI_DIRECTION_RTL)
7884                     {
7885                        x = ln->x;
7886                     }
7887                   else
7888 #endif
7889                     {
7890                        x = ln->x + ln->w;
7891                     }
7892                   y = ln->par->y + ln->y;
7893                }
7894              w = 0;
7895              h = ln->h;
7896           }
7897         else
7898           {
7899              x = ln->x + _ITEM(fi)->x;
7900              y = ln->par->y + ln->y;
7901              w = _ITEM(fi)->w;
7902              h = ln->h;
7903           }
7904      }
7905    else
7906      {
7907         return -1;
7908      }
7909    if (cx) *cx = x;
7910    if (cy) *cy = y;
7911    if (cw) *cw = w;
7912    if (ch) *ch = h;
7913    return ln->par->line_no + ln->line_no;
7914 }
7915
7916 EAPI int
7917 evas_textblock_cursor_char_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
7918 {
7919    return _evas_textblock_cursor_char_pen_geometry_common_get(
7920          cur->ENFN->font_char_coords_get, cur, cx, cy, cw, ch);
7921 }
7922
7923 EAPI int
7924 evas_textblock_cursor_pen_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
7925 {
7926    return _evas_textblock_cursor_char_pen_geometry_common_get(
7927          cur->ENFN->font_pen_coords_get, cur, cx, cy, cw, ch);
7928 }
7929
7930 EAPI int
7931 evas_textblock_cursor_line_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
7932 {
7933    Evas_Object_Textblock *o;
7934    Evas_Object_Textblock_Line *ln = NULL;
7935    Evas_Object_Textblock_Item *it = NULL;
7936    int x, y, w, h;
7937
7938    if (!cur) return -1;
7939    o = (Evas_Object_Textblock *)(cur->obj->object_data);
7940    if (!o->formatted.valid) _relayout(cur->obj);
7941    if (!cur->node)
7942      {
7943         ln = o->paragraphs->lines;
7944      }
7945    else
7946      {
7947         _find_layout_item_match(cur, &ln, &it);
7948      }
7949    if (!ln) return -1;
7950    x = ln->x;
7951    y = ln->par->y + ln->y;
7952    w = ln->w;
7953    h = ln->h;
7954    if (cx) *cx = x;
7955    if (cy) *cy = y;
7956    if (cw) *cw = w;
7957    if (ch) *ch = h;
7958    return ln->par->line_no + ln->line_no;
7959 }
7960
7961 EAPI Eina_Bool
7962 evas_textblock_cursor_visible_range_get(Evas_Textblock_Cursor *start, Evas_Textblock_Cursor *end)
7963 {
7964    Evas *e;
7965    Evas_Coord cy, ch;
7966    Evas_Object *obj = start->obj;
7967    TB_HEAD_RETURN(EINA_FALSE);
7968    e = evas_object_evas_get(obj);
7969    cy = 0 - obj->cur.geometry.y;
7970    ch = e->viewport.h;
7971    evas_textblock_cursor_line_coord_set(start, cy);
7972    evas_textblock_cursor_line_coord_set(end, cy + ch);
7973    evas_textblock_cursor_line_char_last(end);
7974
7975    return EINA_TRUE;
7976 }
7977
7978 EAPI Eina_Bool
7979 evas_textblock_cursor_char_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord x, Evas_Coord y)
7980 {
7981    Evas_Object_Textblock *o;
7982    Evas_Object_Textblock_Paragraph *found_par;
7983    Evas_Object_Textblock_Line *ln;
7984    Evas_Object_Textblock_Item *it = NULL;
7985
7986    if (!cur) return EINA_FALSE;
7987    o = (Evas_Object_Textblock *)(cur->obj->object_data);
7988    if (!o->formatted.valid) _relayout(cur->obj);
7989    x += o->style_pad.l;
7990    y += o->style_pad.t;
7991
7992    found_par = _layout_find_paragraph_by_y(o, y);
7993    if (found_par)
7994      {
7995         _layout_paragraph_render(o, found_par);
7996         EINA_INLIST_FOREACH(found_par->lines, ln)
7997           {
7998              if (ln->par->y + ln->y > y) break;
7999              if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
8000                {
8001                   /* If before or after the line, go to start/end according
8002                    * to paragraph direction. */
8003                   if (x < ln->x)
8004                     {
8005                        cur->pos = ln->items->text_pos;
8006                        cur->node = found_par->text_node;
8007                        if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
8008                          {
8009                             evas_textblock_cursor_line_char_last(cur);
8010                          }
8011                        else
8012                          {
8013                             evas_textblock_cursor_line_char_first(cur);
8014                          }
8015                        return EINA_TRUE;
8016                     }
8017                   else if (x >= ln->x + ln->w)
8018                     {
8019                        cur->pos = ln->items->text_pos;
8020                        cur->node = found_par->text_node;
8021                        if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
8022                          {
8023                             evas_textblock_cursor_line_char_first(cur);
8024                          }
8025                        else
8026                          {
8027                             evas_textblock_cursor_line_char_last(cur);
8028                          }
8029                        return EINA_TRUE;
8030                     }
8031
8032                   EINA_INLIST_FOREACH(ln->items, it)
8033                     {
8034                        if (((it->x + ln->x) <= x) && (((it->x + ln->x) + it->adv) > x))
8035                          {
8036                             if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8037                               {
8038                                  int pos;
8039                                  int cx, cy, cw, ch;
8040                                  Evas_Object_Textblock_Text_Item *ti;
8041                                  ti = _ITEM_TEXT(it);
8042
8043                                  pos = -1;
8044                                  if (ti->parent.format->font.font)
8045                                    pos = cur->ENFN->font_char_at_coords_get(
8046                                          cur->ENDT,
8047                                          ti->parent.format->font.font,
8048                                          &ti->text_props,
8049                                          x - it->x - ln->x, 0,
8050                                          &cx, &cy, &cw, &ch);
8051                                  if (pos < 0)
8052                                    return EINA_FALSE;
8053                                  cur->pos = pos + it->text_pos;
8054                                  cur->node = it->text_node;
8055                                  return EINA_TRUE;
8056                               }
8057                             else
8058                               {
8059                                  Evas_Object_Textblock_Format_Item *fi;
8060                                  fi = _ITEM_FORMAT(it);
8061                                  cur->pos = fi->parent.text_pos;
8062                                  cur->node = found_par->text_node;
8063                                  return EINA_TRUE;
8064                               }
8065                          }
8066                     }
8067                }
8068           }
8069      }
8070    else if (o->paragraphs && (y >= o->paragraphs->y + o->formatted.h))
8071      {
8072         /* If we are after the last paragraph, use the last position in the
8073          * text. */
8074         evas_textblock_cursor_paragraph_last(cur);
8075         return EINA_TRUE;
8076      }
8077    else if (o->paragraphs && (y < o->paragraphs->y))
8078      {
8079         evas_textblock_cursor_paragraph_first(cur);
8080         return EINA_TRUE;
8081      }
8082
8083    return EINA_FALSE;
8084 }
8085
8086 EAPI int
8087 evas_textblock_cursor_line_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord y)
8088 {
8089    Evas_Object_Textblock *o;
8090    Evas_Object_Textblock_Paragraph *found_par;
8091    Evas_Object_Textblock_Line *ln;
8092
8093    if (!cur) return -1;
8094    o = (Evas_Object_Textblock *)(cur->obj->object_data);
8095    if (!o->formatted.valid) _relayout(cur->obj);
8096    y += o->style_pad.t;
8097
8098    found_par = _layout_find_paragraph_by_y(o, y);
8099
8100    if (found_par)
8101      {
8102         _layout_paragraph_render(o, found_par);
8103         EINA_INLIST_FOREACH(found_par->lines, ln)
8104           {
8105              if (ln->par->y + ln->y > y) break;
8106              if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
8107                {
8108                   evas_textblock_cursor_line_set(cur, ln->par->line_no +
8109                         ln->line_no);
8110                   return ln->par->line_no + ln->line_no;
8111                }
8112           }
8113      }
8114    else if (o->paragraphs && (y >= o->paragraphs->y + o->formatted.h))
8115      {
8116         int line_no = 0;
8117         /* If we are after the last paragraph, use the last position in the
8118          * text. */
8119         evas_textblock_cursor_paragraph_last(cur);
8120         if (cur->node && cur->node->par)
8121           {
8122              line_no = cur->node->par->line_no;
8123              if (cur->node->par->lines)
8124                {
8125                   line_no += ((Evas_Object_Textblock_Line *)
8126                         EINA_INLIST_GET(cur->node->par->lines)->last)->line_no;
8127                }
8128           }
8129         return line_no;
8130      }
8131    else if (o->paragraphs && (y < o->paragraphs->y))
8132      {
8133         int line_no = 0;
8134         evas_textblock_cursor_paragraph_first(cur);
8135         if (cur->node && cur->node->par)
8136           {
8137              line_no = cur->node->par->line_no;
8138           }
8139         return line_no;
8140      }
8141    return -1;
8142 }
8143
8144 /**
8145  * @internal
8146  * Updates x and w according to the text direction, position in text and
8147  * if it's a special case switch
8148  *
8149  * @param ti the text item we are working on
8150  * @param x the current x (we get) and the x we return
8151  * @param w the current w (we get) and the w we return
8152  * @param start if this is the first item or not
8153  * @param switch_items toogles item switching (rtl cases)
8154  */
8155 static void
8156 _evas_textblock_range_calc_x_w(const Evas_Object_Textblock_Item *it,
8157       Evas_Coord *x, Evas_Coord *w, Eina_Bool start, Eina_Bool switch_items)
8158 {
8159    if ((start && !switch_items) || (!start && switch_items))
8160      {
8161 #ifdef BIDI_SUPPORT
8162         if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8163             _ITEM_TEXT(it)->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8164             ||
8165             ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8166              _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
8167           {
8168              *w = *x + *w;
8169              *x = 0;
8170           }
8171         else
8172 #endif
8173           {
8174              *w = it->adv - *x;
8175           }
8176      }
8177    else
8178      {
8179 #ifdef BIDI_SUPPORT
8180         if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
8181             _ITEM_TEXT(it)->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8182             ||
8183             ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
8184              _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
8185           {
8186              *x = *x + *w;
8187              *w = it->adv - *x;
8188           }
8189         else
8190 #endif
8191           {
8192              *w = *x;
8193              *x = 0;
8194           }
8195      }
8196
8197 }
8198
8199 /**
8200  * @internal
8201  * Returns the geometry of the range in line ln. Cur1 is the start cursor,
8202  * cur2 is the end cursor, NULL means from the start or to the end accordingly.
8203  * Assumes that ln is valid, and that at least one of cur1 and cur2 is not NULL.
8204  *
8205  * @param ln the line to work on.
8206  * @param cur1 the start cursor
8207  * @param cur2 the end cursor
8208  * @return Returns the geometry of the range
8209  */
8210 static Eina_List *
8211 _evas_textblock_cursor_range_in_line_geometry_get(
8212       const Evas_Object_Textblock_Line *ln, const Evas_Textblock_Cursor *cur1,
8213       const Evas_Textblock_Cursor *cur2)
8214 {
8215    Evas_Object_Textblock_Item *it;
8216    Evas_Object_Textblock_Item *it1, *it2;
8217    Eina_List *rects = NULL;
8218    Evas_Textblock_Rectangle *tr;
8219    size_t start, end;
8220    Eina_Bool switch_items;
8221    const Evas_Textblock_Cursor *cur;
8222
8223    cur = (cur1) ? cur1 : cur2;
8224
8225    /* Find the first and last items */
8226    it1 = it2 = NULL;
8227    start = end = 0;
8228    EINA_INLIST_FOREACH(ln->items, it)
8229      {
8230         size_t item_len;
8231         item_len = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
8232            _ITEM_TEXT(it)->text_props.text_len
8233            : 1;
8234         if ((!cur1 || (cur1->pos < it->text_pos + item_len)) &&
8235               (!cur2 || (cur2->pos >= it->text_pos)))
8236           {
8237              if (!it1)
8238                {
8239                   it1 = it;
8240                   start = item_len; /* start stores the first item_len */
8241                }
8242              it2 = it;
8243              end = item_len; /* end stores the last item_len */
8244           }
8245      }
8246
8247    /* If we couldn't find even one item, return */
8248    if (!it1) return NULL;
8249
8250    /* If the first item is logically before or equal the second item
8251     * we have to set start and end differently than in the other case */
8252    if (it1->text_pos <= it2->text_pos)
8253      {
8254         start = (cur1) ? (cur1->pos - it1->text_pos) : 0;
8255         end = (cur2) ? (cur2->pos - it2->text_pos) : end;
8256         switch_items = EINA_FALSE;
8257      }
8258    else
8259      {
8260         start = (cur2) ? (cur2->pos - it1->text_pos) : start;
8261         end = (cur1) ? (cur1->pos - it2->text_pos) : 0;
8262         switch_items = EINA_TRUE;
8263      }
8264
8265    /* IMPORTANT: Don't use cur1/cur2 past this point (because they probably
8266     * don't make sense anymore. That's why there are start and end),
8267     * unless you know what you are doing */
8268
8269    /* Special case when they share the same item and it's a text item */
8270    if ((it1 == it2) && (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT))
8271      {
8272         Evas_Coord x1, w1, x2, w2;
8273         Evas_Coord x, w, y, h;
8274         Evas_Object_Textblock_Text_Item *ti;
8275         int ret = 0;
8276
8277         ti = _ITEM_TEXT(it1);
8278         if (ti->parent.format->font.font)
8279           {
8280              ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8281                    ti->parent.format->font.font,
8282                    &ti->text_props,
8283                    start,
8284                    &x1, &y, &w1, &h);
8285           }
8286         if (!ret)
8287           {
8288              return NULL;
8289           }
8290         ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8291               ti->parent.format->font.font,
8292               &ti->text_props,
8293               end,
8294               &x2, &y, &w2, &h);
8295         if (!ret)
8296           {
8297              return NULL;
8298           }
8299
8300         /* Make x2 the one on the right */
8301         if (x2 < x1)
8302           {
8303              Evas_Coord tmp;
8304              tmp = x1;
8305              x1 = x2;
8306              x2 = tmp;
8307
8308              tmp = w1;
8309              w1 = w2;
8310              w2 = tmp;
8311           }
8312
8313 #ifdef BIDI_SUPPORT
8314         if (ti->text_props.bidi.dir == EVAS_BIDI_DIRECTION_RTL)
8315           {
8316              x = x1 + w1;
8317              w = x2 + w2 - x;
8318           }
8319         else
8320 #endif
8321           {
8322              x = x1;
8323              w = x2 - x1;
8324           }
8325         if (w > 0)
8326           {
8327              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8328              rects = eina_list_append(rects, tr);
8329              tr->x = ln->x + it1->x + x;
8330              tr->y = ln->par->y + ln->y;
8331              tr->h = ln->h;
8332              tr->w = w;
8333           }
8334      }
8335    else if (it1 != it2)
8336      {
8337         /* Get the middle items */
8338         Evas_Coord min_x, max_x;
8339         Evas_Coord x, w;
8340         it = _ITEM(EINA_INLIST_GET(it1)->next);
8341         min_x = max_x = it->x;
8342
8343         if (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8344           {
8345              Evas_Coord y, h;
8346              Evas_Object_Textblock_Text_Item *ti;
8347              int ret;
8348              ti = _ITEM_TEXT(it1);
8349
8350              ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8351                    ti->parent.format->font.font,
8352                    &ti->text_props,
8353                    start,
8354                    &x, &y, &w, &h);
8355              if (!ret)
8356                {
8357                   /* BUG! Skip the first item */
8358                   x = w = 0;
8359                }
8360              else
8361                {
8362                   _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
8363                         switch_items);
8364                }
8365           }
8366         else
8367           {
8368              x = 0;
8369              w = it1->w;
8370              _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
8371                    switch_items);
8372           }
8373         if (w > 0)
8374           {
8375              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8376              rects = eina_list_append(rects, tr);
8377              tr->x = ln->x + it1->x + x;
8378              tr->y = ln->par->y + ln->y;
8379              tr->h = ln->h;
8380              tr->w = w;
8381           }
8382
8383         while (it && (it != it2))
8384           {
8385              max_x = it->x + it->adv;
8386              it = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(it)->next;
8387           }
8388         if (min_x != max_x)
8389           {
8390              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8391              rects = eina_list_append(rects, tr);
8392              tr->x = ln->x + min_x;
8393              tr->y = ln->par->y + ln->y;
8394              tr->h = ln->h;
8395              tr->w = max_x - min_x;
8396           }
8397         if (it2->type == EVAS_TEXTBLOCK_ITEM_TEXT)
8398           {
8399              Evas_Coord y, h;
8400              Evas_Object_Textblock_Text_Item *ti;
8401              int ret;
8402              ti = _ITEM_TEXT(it2);
8403
8404              ret = cur->ENFN->font_pen_coords_get(cur->ENDT,
8405                    ti->parent.format->font.font,
8406                    &ti->text_props,
8407                    end,
8408                    &x, &y, &w, &h);
8409              if (!ret)
8410                {
8411                   /* BUG! skip the last item */
8412                   x = w = 0;
8413                }
8414              else
8415                {
8416                   _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
8417                         switch_items);
8418                }
8419           }
8420         else
8421           {
8422              x = 0;
8423              w = it2->w;
8424              _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
8425                         switch_items);
8426           }
8427         if (w > 0)
8428           {
8429              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8430              rects = eina_list_append(rects, tr);
8431              tr->x = ln->x + it2->x + x;
8432              tr->y = ln->par->y + ln->y;
8433              tr->h = ln->h;
8434              tr->w = w;
8435           }
8436      }
8437    return rects;
8438 }
8439 EAPI Eina_List *
8440 evas_textblock_cursor_range_geometry_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
8441 {
8442    Evas_Object_Textblock *o;
8443    Evas_Object_Textblock_Line *ln1, *ln2;
8444    Evas_Object_Textblock_Item *it1, *it2;
8445    Eina_List *rects = NULL;
8446    Evas_Textblock_Rectangle *tr;
8447
8448    if (!cur1 || !cur1->node) return NULL;
8449    if (!cur2 || !cur2->node) return NULL;
8450    if (cur1->obj != cur2->obj) return NULL;
8451    o = (Evas_Object_Textblock *)(cur1->obj->object_data);
8452    if (!o->formatted.valid) _relayout(cur1->obj);
8453    if (evas_textblock_cursor_compare(cur1, cur2) > 0)
8454      {
8455         const Evas_Textblock_Cursor *tc;
8456
8457         tc = cur1;
8458         cur1 = cur2;
8459         cur2 = tc;
8460      }
8461
8462    ln1 = ln2 = NULL;
8463    it1 = it2 = NULL;
8464    _find_layout_item_match(cur1, &ln1, &it1);
8465    if (!ln1 || !it1) return NULL;
8466    _find_layout_item_match(cur2, &ln2, &it2);
8467    if (!ln2 || !it2) return NULL;
8468
8469    if (ln1 == ln2)
8470      {
8471         rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
8472               cur1, cur2);
8473      }
8474    else
8475      {
8476         Evas_Object_Textblock_Line *plni, *lni;
8477         Eina_List *rects2 = NULL;
8478         /* Handle the first line */
8479         rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
8480               cur1, NULL);
8481
8482         /* Handle the lines between the first and the last line */
8483         lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(ln1)->next;
8484         if (!lni && (ln1->par != ln2->par))
8485           {
8486              lni = ((Evas_Object_Textblock_Paragraph *)
8487                     EINA_INLIST_GET(ln1->par)->next)->lines;
8488           }
8489         while (lni && (lni != ln2))
8490           {
8491              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
8492              rects = eina_list_append(rects, tr);
8493              tr->x = lni->x;
8494              tr->y = lni->par->y + lni->y;
8495              tr->h = lni->h;
8496              tr->w = lni->w;
8497              plni = lni;
8498              lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(lni)->next;
8499              if (!lni && (plni->par != ln2->par))
8500                {
8501                   lni = ((Evas_Object_Textblock_Paragraph *)
8502                      EINA_INLIST_GET(plni->par)->next)->lines;
8503                }
8504           }
8505         rects2 = _evas_textblock_cursor_range_in_line_geometry_get(ln2,
8506               NULL, cur2);
8507         rects = eina_list_merge(rects, rects2);
8508      }
8509    return rects;
8510 }
8511
8512 EAPI Eina_Bool
8513 evas_textblock_cursor_format_item_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8514 {
8515    Evas_Object_Textblock *o;
8516    Evas_Object_Textblock_Line *ln = NULL;
8517    Evas_Object_Textblock_Format_Item *fi;
8518    Evas_Object_Textblock_Item *it = NULL;
8519    Evas_Coord x, y, w, h;
8520
8521    if (!cur || !evas_textblock_cursor_format_is_visible_get(cur)) return EINA_FALSE;
8522    o = (Evas_Object_Textblock *)(cur->obj->object_data);
8523    if (!o->formatted.valid) _relayout(cur->obj);
8524    if (!evas_textblock_cursor_format_is_visible_get(cur)) return EINA_FALSE;
8525    _find_layout_item_line_match(cur->obj, cur->node, cur->pos, &ln, &it);
8526    fi = _ITEM_FORMAT(it);
8527    if ((!ln) || (!fi)) return EINA_FALSE;
8528    x = ln->x + fi->parent.x;
8529    y = ln->par->y + ln->y + ln->baseline + fi->y;
8530    w = fi->parent.w;
8531    h = fi->parent.h;
8532    if (cx) *cx = x;
8533    if (cy) *cy = y;
8534    if (cw) *cw = w;
8535    if (ch) *ch = h;
8536    return EINA_TRUE;
8537 }
8538
8539 EAPI Eina_Bool
8540 evas_textblock_cursor_eol_get(const Evas_Textblock_Cursor *cur)
8541 {
8542    Eina_Bool ret = EINA_FALSE;
8543    Evas_Textblock_Cursor cur2;
8544    if (!cur) return EINA_FALSE;
8545
8546    cur2.obj = cur->obj;
8547    evas_textblock_cursor_copy(cur, &cur2);
8548    evas_textblock_cursor_line_char_last(&cur2);
8549    if (cur2.pos == cur->pos)
8550      {
8551         ret = EINA_TRUE;
8552      }
8553    return ret;
8554 }
8555
8556 /* general controls */
8557 EAPI Eina_Bool
8558 evas_object_textblock_line_number_geometry_get(const Evas_Object *obj, int line, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
8559 {
8560    Evas_Object_Textblock_Line *ln;
8561
8562    TB_HEAD_RETURN(0);
8563    ln = _find_layout_line_num(obj, line);
8564    if (!ln) return EINA_FALSE;
8565    if (cx) *cx = ln->x;
8566    if (cy) *cy = ln->par->y + ln->y;
8567    if (cw) *cw = ln->w;
8568    if (ch) *ch = ln->h;
8569    return EINA_TRUE;
8570 }
8571
8572 EAPI void
8573 evas_object_textblock_clear(Evas_Object *obj)
8574 {
8575    Eina_List *l;
8576    Evas_Textblock_Cursor *cur;
8577
8578    TB_HEAD();
8579    if (o->paragraphs)
8580      {
8581         _paragraphs_free(obj, o->paragraphs);
8582         o->paragraphs = NULL;
8583      }
8584
8585    _nodes_clear(obj);
8586    o->cursor->node = NULL;
8587    o->cursor->pos = 0;
8588    EINA_LIST_FOREACH(o->cursors, l, cur)
8589      {
8590         cur->node = NULL;
8591         cur->pos = 0;
8592
8593      }
8594    _evas_textblock_changed(o, obj);
8595 }
8596
8597 EAPI void
8598 evas_object_textblock_size_formatted_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
8599 {
8600    TB_HEAD();
8601    if (!o->formatted.valid) _relayout(obj);
8602    if (w) *w = o->formatted.w;
8603    if (h) *h = o->formatted.h;
8604 }
8605
8606 static void
8607 _size_native_calc_line_finalize(const Evas_Object *obj, Eina_List *items,
8608       Evas_Coord *ascent, Evas_Coord *descent, Evas_Coord *w)
8609 {
8610    Evas_Object_Textblock_Item *it;
8611    Eina_List *i;
8612
8613    it = eina_list_data_get(items);
8614    /* If there are no text items yet, calc ascent/descent
8615     * according to the current format. */
8616    if (it && (*ascent + *descent == 0))
8617       _layout_format_ascent_descent_adjust(obj, ascent, descent, it->format);
8618
8619    *w = 0;
8620    /* Adjust all the item sizes according to the final line size,
8621     * and update the x positions of all the items of the line. */
8622    EINA_LIST_FOREACH(items, i, it)
8623      {
8624         if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
8625           {
8626              Evas_Coord fw, fh, fy;
8627
8628              Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
8629              if (!fi->formatme) goto loop_advance;
8630              _layout_calculate_format_item_size(obj, fi, ascent,
8631                    descent, &fy, &fw, &fh);
8632           }
8633
8634 loop_advance:
8635         *w += it->adv;
8636      }
8637 }
8638
8639 /* FIXME: doc */
8640 static void
8641 _size_native_calc_paragraph_size(const Evas_Object *obj,
8642       const Evas_Object_Textblock *o,
8643       const Evas_Object_Textblock_Paragraph *par,
8644       Evas_Coord *_w, Evas_Coord *_h)
8645 {
8646    Eina_List *i;
8647    Evas_Object_Textblock_Item *it;
8648    Eina_List *line_items = NULL;
8649    Evas_Coord w = 0, y = 0, wmax = 0, h = 0, ascent = 0, descent = 0;
8650
8651    EINA_LIST_FOREACH(par->logical_items, i, it)
8652      {
8653         line_items = eina_list_append(line_items, it);
8654         if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
8655           {
8656              Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
8657              if (fi->item && (_IS_LINE_SEPARATOR(fi->item) ||
8658                       _IS_PARAGRAPH_SEPARATOR(o, fi->item)))
8659                {
8660                   _size_native_calc_line_finalize(obj, line_items, &ascent,
8661                         &descent, &w);
8662
8663                   if (ascent + descent > h)
8664                      h = ascent + descent;
8665
8666                   y += h;
8667                   if (w > wmax)
8668                      wmax = w;
8669                   h = 0;
8670                   ascent = descent = 0;
8671                   line_items = eina_list_free(line_items);
8672                }
8673              else
8674                {
8675                   Evas_Coord fw, fh, fy;
8676                   /* If there are no text items yet, calc ascent/descent
8677                    * according to the current format. */
8678                   if (it && (ascent + descent == 0))
8679                      _layout_format_ascent_descent_adjust(obj, &ascent,
8680                            &descent, it->format);
8681
8682                   _layout_calculate_format_item_size(obj, fi, &ascent,
8683                         &descent, &fy, &fw, &fh);
8684                }
8685           }
8686         else
8687           {
8688              _layout_format_ascent_descent_adjust(obj, &ascent,
8689                    &descent, it->format);
8690           }
8691      }
8692
8693    _size_native_calc_line_finalize(obj, line_items, &ascent, &descent, &w);
8694
8695    line_items = eina_list_free(line_items);
8696
8697    /* Do the last addition */
8698    if (ascent + descent > h)
8699       h = ascent + descent;
8700
8701    if (w > wmax)
8702       wmax = w;
8703
8704    *_h = y + h;
8705    *_w = wmax;
8706 }
8707
8708 EAPI void
8709 evas_object_textblock_size_native_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
8710 {
8711    TB_HEAD();
8712    if (!o->native.valid)
8713      {
8714         Evas_Coord wmax = 0, hmax = 0;
8715         Evas_Object_Textblock_Paragraph *par;
8716         /* We just want the layout objects to update, should probably
8717          * split that. */
8718         if (!o->formatted.valid) _relayout(obj);
8719         EINA_INLIST_FOREACH(o->paragraphs, par)
8720           {
8721              Evas_Coord tw, th;
8722              _size_native_calc_paragraph_size(obj, o, par, &tw, &th);
8723              if (tw > wmax)
8724                 wmax = tw;
8725              hmax += th;
8726           }
8727
8728         o->native.w = wmax;
8729         o->native.h = hmax;
8730
8731         o->native.valid = 1;
8732         o->content_changed = 0;
8733         o->format_changed = EINA_FALSE;
8734      }
8735    if (w) *w = o->native.w;
8736    if (h) *h = o->native.h;
8737 }
8738
8739 EAPI void
8740 evas_object_textblock_style_insets_get(const Evas_Object *obj, Evas_Coord *l, Evas_Coord *r, Evas_Coord *t, Evas_Coord *b)
8741 {
8742    TB_HEAD();
8743    if (!o->formatted.valid) _relayout(obj);
8744    if (l) *l = o->style_pad.l;
8745    if (r) *r = o->style_pad.r;
8746    if (t) *t = o->style_pad.t;
8747    if (b) *b = o->style_pad.b;
8748 }
8749
8750 /** @internal
8751  * FIXME: DELETE ME! DELETE ME!
8752  * This is an ugly workaround to get around the fact that
8753  * evas_object_textblock_coords_recalc isn't really called when it's supposed
8754  * to. When that bug is fixed please remove this. */
8755 static void
8756 _workaround_object_coords_recalc(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
8757 {
8758    evas_object_textblock_coords_recalc(obj);
8759 }
8760
8761 /* all nice and private */
8762 static void
8763 evas_object_textblock_init(Evas_Object *obj)
8764 {
8765    Evas_Object_Textblock *o;
8766 #ifdef HAVE_LINEBREAK
8767    static Eina_Bool linebreak_init = EINA_FALSE;
8768    if (!linebreak_init)
8769      {
8770         linebreak_init = EINA_TRUE;
8771         init_linebreak();
8772      }
8773 #endif
8774
8775    /* alloc image ob, setup methods and default values */
8776    obj->object_data = evas_object_textblock_new();
8777    /* set up default settings for this kind of object */
8778    obj->cur.color.r = 255;
8779    obj->cur.color.g = 255;
8780    obj->cur.color.b = 255;
8781    obj->cur.color.a = 255;
8782    obj->cur.geometry.x = 0.0;
8783    obj->cur.geometry.y = 0.0;
8784    obj->cur.geometry.w = 0.0;
8785    obj->cur.geometry.h = 0.0;
8786    obj->cur.layer = 0;
8787    /* set up object-specific settings */
8788    obj->prev = obj->cur;
8789    /* set up methods (compulsory) */
8790    obj->func = &object_func;
8791    obj->type = o_type;
8792
8793    o = (Evas_Object_Textblock *)(obj->object_data);
8794    o->cursor->obj = obj;
8795    o->legacy_newline = EINA_TRUE;
8796    evas_object_event_callback_priority_add(obj, EVAS_CALLBACK_RESIZE, -1000,
8797          _workaround_object_coords_recalc, NULL);
8798 }
8799
8800 static void *
8801 evas_object_textblock_new(void)
8802 {
8803    Evas_Object_Textblock *o;
8804
8805    /* alloc obj private data */
8806    EVAS_MEMPOOL_INIT(_mp_obj, "evas_object_textblock", Evas_Object_Textblock, 64, NULL);
8807    o = EVAS_MEMPOOL_ALLOC(_mp_obj, Evas_Object_Textblock);
8808    if (!o) return NULL;
8809    EVAS_MEMPOOL_PREP(_mp_obj, o, Evas_Object_Textblock);
8810    o->magic = MAGIC_OBJ_TEXTBLOCK;
8811    o->cursor = calloc(1, sizeof(Evas_Textblock_Cursor));
8812    _format_command_init();
8813    return o;
8814 }
8815
8816 static void
8817 evas_object_textblock_free(Evas_Object *obj)
8818 {
8819    Evas_Object_Textblock *o;
8820
8821    evas_object_textblock_clear(obj);
8822    evas_object_textblock_style_set(obj, NULL);
8823    o = (Evas_Object_Textblock *)(obj->object_data);
8824    free(o->cursor);
8825    while (o->cursors)
8826      {
8827         Evas_Textblock_Cursor *cur;
8828
8829         cur = (Evas_Textblock_Cursor *)o->cursors->data;
8830         o->cursors = eina_list_remove_list(o->cursors, o->cursors);
8831         free(cur);
8832      }
8833    if (o->repch) eina_stringshare_del(o->repch);
8834    if (o->ellip_ti) _item_free(obj, NULL, _ITEM(o->ellip_ti));
8835    o->magic = 0;
8836    EVAS_MEMPOOL_FREE(_mp_obj, o);
8837   _format_command_shutdown();
8838 }
8839
8840
8841 static void
8842 evas_object_textblock_render(Evas_Object *obj, void *output, void *context, void *surface, int x, int y)
8843 {
8844    Evas_Object_Textblock_Paragraph *par, *start = NULL;
8845    Evas_Object_Textblock_Line *ln;
8846    Evas_Object_Textblock *o;
8847    int i, j;
8848    unsigned char r = 0, g = 0, b = 0, a = 0;
8849    unsigned char r2 = 0, g2 = 0, b2 = 0, a2 = 0;
8850    unsigned char r3 = 0, g3 = 0, b3 = 0, a3 = 0;
8851    int cx, cy, cw, ch, clip;
8852    const char vals[5][5] =
8853      {
8854           {0, 1, 2, 1, 0},
8855           {1, 3, 4, 3, 1},
8856           {2, 4, 5, 4, 2},
8857           {1, 3, 4, 3, 1},
8858           {0, 1, 2, 1, 0}
8859      };
8860
8861    /* render object to surface with context, and offxet by x,y */
8862    o = (Evas_Object_Textblock *)(obj->object_data);
8863    obj->layer->evas->engine.func->context_multiplier_unset(output,
8864                                                            context);
8865    /* FIXME: This clipping is just until we fix inset handling correctly. */
8866    ENFN->context_clip_clip(output, context,
8867                               obj->cur.geometry.x + x,
8868                               obj->cur.geometry.y + y,
8869                               obj->cur.geometry.w,
8870                               obj->cur.geometry.h);
8871    clip = ENFN->context_clip_get(output, context, &cx, &cy, &cw, &ch);
8872    /* If there are no paragraphs and thus there are no lines,
8873     * there's nothing left to do. */
8874    if (!o->paragraphs) return;
8875
8876 #define ITEM_WALK() \
8877    EINA_INLIST_FOREACH(start, par) \
8878      { \
8879         if (!par->visible) continue; \
8880         if (clip) \
8881           { \
8882              if ((obj->cur.geometry.y + y + par->y + par->h) < (cy - 20)) \
8883              continue; \
8884              if ((obj->cur.geometry.y + y + par->y) > (cy + ch + 20)) \
8885              break; \
8886           } \
8887         _layout_paragraph_render(o, par); \
8888         EINA_INLIST_FOREACH(par->lines, ln) \
8889           { \
8890              Evas_Object_Textblock_Item *itr; \
8891              \
8892              if (clip) \
8893                { \
8894                   if ((obj->cur.geometry.y + y + par->y + ln->y + ln->h) < (cy - 20)) \
8895                   continue; \
8896                   if ((obj->cur.geometry.y + y + par->y + ln->y) > (cy + ch + 20)) \
8897                   break; \
8898                } \
8899              EINA_INLIST_FOREACH(ln->items, itr) \
8900                { \
8901                   Evas_Coord yoff; \
8902                   yoff = ln->baseline; \
8903                   if (itr->format->valign != -1.0) \
8904                     { \
8905                        yoff += itr->format->valign * (ln->h - itr->h); \
8906                     } \
8907                   if (clip) \
8908                     { \
8909                        if ((obj->cur.geometry.x + x + ln->x + itr->x + itr->w) < (cx - 20)) \
8910                        continue; \
8911                        if ((obj->cur.geometry.x + x + ln->x + itr->x) > (cx + cw + 20)) \
8912                        break; \
8913                     } \
8914                   if ((ln->x + itr->x + itr->w) <= 0) continue; \
8915                   if (ln->x + itr->x > obj->cur.geometry.w) break; \
8916                   do
8917
8918 #define ITEM_WALK_END() \
8919                   while (0); \
8920                } \
8921           } \
8922      } \
8923    do {} while(0)
8924 #define COLOR_SET(col) \
8925    ENFN->context_color_set(output, context, \
8926          (obj->cur.cache.clip.r * ti->parent.format->color.col.r) / 255, \
8927          (obj->cur.cache.clip.g * ti->parent.format->color.col.g) / 255, \
8928          (obj->cur.cache.clip.b * ti->parent.format->color.col.b) / 255, \
8929          (obj->cur.cache.clip.a * ti->parent.format->color.col.a) / 255);
8930 #define COLOR_SET_AMUL(col, amul) \
8931    ENFN->context_color_set(output, context, \
8932          (obj->cur.cache.clip.r * ti->parent.format->color.col.r * (amul)) / 65025, \
8933          (obj->cur.cache.clip.g * ti->parent.format->color.col.g * (amul)) / 65025, \
8934          (obj->cur.cache.clip.b * ti->parent.format->color.col.b * (amul)) / 65025, \
8935          (obj->cur.cache.clip.a * ti->parent.format->color.col.a * (amul)) / 65025);
8936 #define DRAW_TEXT(ox, oy) \
8937    if (ti->parent.format->font.font) ENFN->font_draw(output, context, surface, ti->parent.format->font.font, \
8938          obj->cur.geometry.x + ln->x + ti->parent.x + x + (ox), \
8939          obj->cur.geometry.y + ln->par->y + ln->y + yoff + y + (oy), \
8940          ti->parent.w, ti->parent.h, ti->parent.w, ti->parent.h, \
8941          &ti->text_props);
8942
8943    /* backing */
8944 #define DRAW_RECT(ox, oy, ow, oh, or, og, ob, oa) \
8945    do \
8946      { \
8947         ENFN->context_color_set(output, \
8948               context, \
8949               (obj->cur.cache.clip.r * or) / 255, \
8950               (obj->cur.cache.clip.g * og) / 255, \
8951               (obj->cur.cache.clip.b * ob) / 255, \
8952               (obj->cur.cache.clip.a * oa) / 255); \
8953         ENFN->rectangle_draw(output, \
8954               context, \
8955               surface, \
8956               obj->cur.geometry.x + ln->x + x + (ox), \
8957               obj->cur.geometry.y + ln->par->y + ln->y + y + (oy), \
8958               (ow), \
8959               (oh)); \
8960      } \
8961    while (0)
8962
8963 #define DRAW_FORMAT(oname, oy, oh, or, og, ob, oa) \
8964    do \
8965      { \
8966         if (itr->format->oname) \
8967           { \
8968              or = itr->format->color.oname.r; \
8969              og = itr->format->color.oname.g; \
8970              ob = itr->format->color.oname.b; \
8971              oa = itr->format->color.oname.a; \
8972              if (!EINA_INLIST_GET(itr)->next) \
8973                { \
8974                   DRAW_RECT(itr->x, oy, itr->w, oh, or, og, ob, oa); \
8975                } \
8976              else \
8977                { \
8978                   DRAW_RECT(itr->x, oy, itr->adv, oh, or, og, ob, oa); \
8979                } \
8980           } \
8981      } \
8982    while (0)
8983
8984      {
8985         Evas_Coord look_for_y = 0 - (obj->cur.geometry.y + y);
8986         if (clip)
8987           {
8988              Evas_Coord tmp_lfy = cy - (obj->cur.geometry.y + y);
8989              if (tmp_lfy > look_for_y)
8990                 look_for_y = tmp_lfy;
8991           }
8992
8993         if (look_for_y >= 0)
8994            start = _layout_find_paragraph_by_y(o, look_for_y);
8995
8996         if (!start)
8997            start = o->paragraphs;
8998      }
8999
9000    ITEM_WALK()
9001      {
9002         DRAW_FORMAT(backing, 0, ln->h, r, g, b, a);
9003      }
9004    ITEM_WALK_END();
9005
9006    /* There are size adjustments that depend on the styles drawn here back
9007     * in "_text_item_update_sizes" should not modify one without the other. */
9008
9009    /* prepare everything for text draw */
9010
9011    /* shadows */
9012    ITEM_WALK()
9013      {
9014         int shad_dst, shad_sz, dx, dy, haveshad;
9015         Evas_Object_Textblock_Text_Item *ti;
9016         ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9017         if (!ti) continue;
9018
9019         shad_dst = shad_sz = dx = dy = haveshad = 0;
9020         switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC)
9021           {
9022            case EVAS_TEXT_STYLE_SHADOW:
9023            case EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW:
9024              shad_dst = 1;
9025              haveshad = 1;
9026              break;
9027            case EVAS_TEXT_STYLE_OUTLINE_SHADOW:
9028            case EVAS_TEXT_STYLE_FAR_SHADOW:
9029              shad_dst = 2;
9030              haveshad = 1;
9031              break;
9032            case EVAS_TEXT_STYLE_FAR_SOFT_SHADOW:
9033              shad_dst = 2;
9034              shad_sz = 2;
9035              haveshad = 1;
9036              break;
9037            case EVAS_TEXT_STYLE_SOFT_SHADOW:
9038              shad_dst = 1;
9039              shad_sz = 2;
9040              haveshad = 1;
9041              break;
9042            default:
9043              break;
9044           }
9045         if (haveshad)
9046           {
9047              if (shad_dst > 0)
9048                {
9049                   switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_SHADOW_DIRECTION)
9050                     {
9051                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT:
9052                        dx = 1;
9053                        dy = 1;
9054                        break;
9055                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM:
9056                        dx = 0;
9057                        dy = 1;
9058                        break;
9059                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT:
9060                        dx = -1;
9061                        dy = 1;
9062                        break;
9063                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT:
9064                        dx = -1;
9065                        dy = 0;
9066                        break;
9067                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT:
9068                        dx = -1;
9069                        dy = -1;
9070                        break;
9071                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP:
9072                        dx = 0;
9073                        dy = -1;
9074                        break;
9075                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT:
9076                        dx = 1;
9077                        dy = -1;
9078                        break;
9079                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT:
9080                        dx = 1;
9081                        dy = 0;
9082                      default:
9083                        break;
9084                     }
9085                   dx *= shad_dst;
9086                   dy *= shad_dst;
9087                }
9088              switch (shad_sz)
9089                {
9090                 case 0:
9091                   COLOR_SET(shadow);
9092                   DRAW_TEXT(dx, dy);
9093                   break;
9094                 case 2:
9095                   for (j = 0; j < 5; j++)
9096                     {
9097                        for (i = 0; i < 5; i++)
9098                          {
9099                             if (vals[i][j] != 0)
9100                               {
9101                                  COLOR_SET_AMUL(shadow, vals[i][j] * 50);
9102                                  DRAW_TEXT(i - 2 + dx, j - 2 + dy);
9103                               }
9104                          }
9105                     }
9106                   break;
9107                 default:
9108                   break;
9109                }
9110           }
9111      }
9112    ITEM_WALK_END();
9113
9114    /* glows */
9115    ITEM_WALK()
9116      {
9117         Evas_Object_Textblock_Text_Item *ti;
9118         ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9119         if (!ti) continue;
9120
9121         if (ti->parent.format->style == EVAS_TEXT_STYLE_GLOW)
9122           {
9123              for (j = 0; j < 5; j++)
9124                {
9125                   for (i = 0; i < 5; i++)
9126                     {
9127                        if (vals[i][j] != 0)
9128                          {
9129                             COLOR_SET_AMUL(glow, vals[i][j] * 50);
9130                             DRAW_TEXT(i - 2, j - 2);
9131                          }
9132                     }
9133                }
9134              COLOR_SET(glow2);
9135              DRAW_TEXT(-1, 0);
9136              DRAW_TEXT(1, 0);
9137              DRAW_TEXT(0, -1);
9138              DRAW_TEXT(0, 1);
9139           }
9140      }
9141    ITEM_WALK_END();
9142
9143    /* outlines */
9144    ITEM_WALK()
9145      {
9146         Evas_Object_Textblock_Text_Item *ti;
9147         ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9148         if (!ti) continue;
9149
9150         if ((ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE) ||
9151               (ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE_SHADOW) ||
9152               (ti->parent.format->style == EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW))
9153           {
9154              COLOR_SET(outline);
9155              DRAW_TEXT(-1, 0);
9156              DRAW_TEXT(1, 0);
9157              DRAW_TEXT(0, -1);
9158              DRAW_TEXT(0, 1);
9159           }
9160         else if (ti->parent.format->style == EVAS_TEXT_STYLE_SOFT_OUTLINE)
9161           {
9162              for (j = 0; j < 5; j++)
9163                {
9164                   for (i = 0; i < 5; i++)
9165                     {
9166                        if (((i != 2) || (j != 2)) && (vals[i][j] != 0))
9167                          {
9168                             COLOR_SET_AMUL(outline, vals[i][j] * 50);
9169                             DRAW_TEXT(i - 2, j - 2);
9170                          }
9171                     }
9172                }
9173           }
9174      }
9175    ITEM_WALK_END();
9176
9177    /* normal text and lines */
9178    ITEM_WALK()
9179      {
9180         Evas_Object_Textblock_Text_Item *ti;
9181         ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
9182         /* NORMAL TEXT */
9183         if (ti)
9184           {
9185              COLOR_SET(normal);
9186              DRAW_TEXT(0, 0);
9187           }
9188
9189         /* STRIKETHROUGH */
9190         DRAW_FORMAT(strikethrough, (ln->h / 2), 1, r, g, b, a);
9191
9192         /* UNDERLINE */
9193         DRAW_FORMAT(underline, ln->baseline + 1, 1, r2, g2, b2, a2);
9194
9195         /* UNDERLINE2 */
9196         DRAW_FORMAT(underline2, ln->baseline + 3, 1, r3, g3, b3, a3);
9197      }
9198    ITEM_WALK_END();
9199 }
9200
9201 static void
9202 evas_object_textblock_render_pre(Evas_Object *obj)
9203 {
9204    Evas_Object_Textblock *o;
9205    int is_v, was_v;
9206
9207    /* dont pre-render the obj twice! */
9208    if (obj->pre_render_done) return;
9209    obj->pre_render_done = 1;
9210    /* pre-render phase. this does anything an object needs to do just before */
9211    /* rendering. this could mean loading the image data, retrieving it from */
9212    /* elsewhere, decoding video etc. */
9213    /* then when this is done the object needs to figure if it changed and */
9214    /* if so what and where and add the appropriate redraw textblocks */
9215    o = (Evas_Object_Textblock *)(obj->object_data);
9216    if ((o->changed) || (o->content_changed) || (o->format_changed) ||
9217        ((obj->cur.geometry.w != o->last_w) ||
9218            (((o->valign != 0.0) || (o->have_ellipsis)) &&
9219                (obj->cur.geometry.h != o->last_h))))
9220      {
9221         _relayout(obj);
9222         o->redraw = 0;
9223         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9224         is_v = evas_object_is_visible(obj);
9225         was_v = evas_object_was_visible(obj);
9226         goto done;
9227      }
9228    if (o->redraw)
9229      {
9230         o->redraw = 0;
9231         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9232         is_v = evas_object_is_visible(obj);
9233         was_v = evas_object_was_visible(obj);
9234         goto done;
9235      }
9236    /* if someone is clipping this obj - go calculate the clipper */
9237    if (obj->cur.clipper)
9238      {
9239         if (obj->cur.cache.clip.dirty)
9240           evas_object_clip_recalc(obj->cur.clipper);
9241         obj->cur.clipper->func->render_pre(obj->cur.clipper);
9242      }
9243    /* now figure what changed and add draw rects */
9244    /* if it just became visible or invisible */
9245    is_v = evas_object_is_visible(obj);
9246    was_v = evas_object_was_visible(obj);
9247    if (is_v != was_v)
9248      {
9249         evas_object_render_pre_visible_change(&obj->layer->evas->clip_changes, obj, is_v, was_v);
9250         goto done;
9251      }
9252    if ((obj->cur.map != obj->prev.map) ||
9253        (obj->cur.usemap != obj->prev.usemap))
9254      {
9255         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9256         goto done;
9257      }
9258    /* it's not visible - we accounted for it appearing or not so just abort */
9259    if (!is_v) goto done;
9260    /* clipper changed this is in addition to anything else for obj */
9261    evas_object_render_pre_clipper_change(&obj->layer->evas->clip_changes, obj);
9262    /* if we restacked (layer or just within a layer) and don't clip anyone */
9263    if (obj->restack)
9264      {
9265         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9266         goto done;
9267      }
9268    /* if it changed color */
9269    if ((obj->cur.color.r != obj->prev.color.r) ||
9270        (obj->cur.color.g != obj->prev.color.g) ||
9271        (obj->cur.color.b != obj->prev.color.b) ||
9272        (obj->cur.color.a != obj->prev.color.a))
9273      {
9274         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9275         goto done;
9276      }
9277    /* if it changed geometry - and obviously not visibility or color */
9278    /* calculate differences since we have a constant color fill */
9279    /* we really only need to update the differences */
9280    if ((obj->cur.geometry.x != obj->prev.geometry.x) ||
9281        (obj->cur.geometry.y != obj->prev.geometry.y) ||
9282        (obj->cur.geometry.w != obj->prev.geometry.w) ||
9283        (obj->cur.geometry.h != obj->prev.geometry.h))
9284      {
9285         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
9286         goto done;
9287      }
9288    done:
9289    evas_object_render_pre_effect_updates(&obj->layer->evas->clip_changes, obj, is_v, was_v);
9290 }
9291
9292 static void
9293 evas_object_textblock_render_post(Evas_Object *obj)
9294 {
9295 /*   Evas_Object_Textblock *o; */
9296
9297    /* this moves the current data to the previous state parts of the object */
9298    /* in whatever way is safest for the object. also if we don't need object */
9299    /* data anymore we can free it if the object deems this is a good idea */
9300 /*   o = (Evas_Object_Textblock *)(obj->object_data); */
9301    /* remove those pesky changes */
9302    evas_object_clip_changes_clean(obj);
9303    /* move cur to prev safely for object data */
9304    obj->prev = obj->cur;
9305 /*   o->prev = o->cur; */
9306 /*   o->changed = 0; */
9307 }
9308
9309 static unsigned int evas_object_textblock_id_get(Evas_Object *obj)
9310 {
9311    Evas_Object_Textblock *o;
9312
9313    o = (Evas_Object_Textblock *)(obj->object_data);
9314    if (!o) return 0;
9315    return MAGIC_OBJ_TEXTBLOCK;
9316 }
9317
9318 static unsigned int evas_object_textblock_visual_id_get(Evas_Object *obj)
9319 {
9320    Evas_Object_Textblock *o;
9321
9322    o = (Evas_Object_Textblock *)(obj->object_data);
9323    if (!o) return 0;
9324    return MAGIC_OBJ_CUSTOM;
9325 }
9326
9327 static void *evas_object_textblock_engine_data_get(Evas_Object *obj)
9328 {
9329    Evas_Object_Textblock *o;
9330
9331    o = (Evas_Object_Textblock *)(obj->object_data);
9332    if (!o) return NULL;
9333    return o->engine_data;
9334 }
9335
9336 static int
9337 evas_object_textblock_is_opaque(Evas_Object *obj __UNUSED__)
9338 {
9339    /* this returns 1 if the internal object data implies that the object is */
9340    /* currently fulyl opque over the entire gradient it occupies */
9341    return 0;
9342 }
9343
9344 static int
9345 evas_object_textblock_was_opaque(Evas_Object *obj __UNUSED__)
9346 {
9347    /* this returns 1 if the internal object data implies that the object was */
9348    /* currently fulyl opque over the entire gradient it occupies */
9349    return 0;
9350 }
9351
9352 static void
9353 evas_object_textblock_coords_recalc(Evas_Object *obj)
9354 {
9355    Evas_Object_Textblock *o;
9356
9357    o = (Evas_Object_Textblock *)(obj->object_data);
9358    if ((obj->cur.geometry.w != o->last_w) ||
9359        (((o->valign != 0.0) || (o->have_ellipsis)) &&
9360            (obj->cur.geometry.h != o->last_h)))
9361      {
9362         o->formatted.valid = 0;
9363         o->changed = 1;
9364      }
9365 }
9366
9367 static void
9368 evas_object_textblock_scale_update(Evas_Object *obj)
9369 {
9370    Evas_Object_Textblock *o;
9371
9372    o = (Evas_Object_Textblock *)(obj->object_data);
9373    _evas_textblock_invalidate_all(o);
9374    _evas_textblock_changed(o, obj);
9375 }
9376
9377 void
9378 _evas_object_textblock_rehint(Evas_Object *obj)
9379 {
9380    Evas_Object_Textblock *o;
9381    Evas_Object_Textblock_Paragraph *par;
9382    Evas_Object_Textblock_Line *ln;
9383
9384    o = (Evas_Object_Textblock *)(obj->object_data);
9385    EINA_INLIST_FOREACH(o->paragraphs, par)
9386      {
9387         EINA_INLIST_FOREACH(par->lines, ln)
9388           {
9389              Evas_Object_Textblock_Item *it;
9390
9391              EINA_INLIST_FOREACH(ln->items, it)
9392                {
9393                   if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
9394                     {
9395                        Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
9396                        if (ti->parent.format->font.font)
9397                          {  
9398 #ifdef EVAS_FRAME_QUEUING
9399                             evas_common_pipe_op_text_flush((RGBA_Font *) ti->parent.format->font.font);
9400 #endif
9401                             evas_font_load_hinting_set(obj->layer->evas,
9402                                   ti->parent.format->font.font,
9403                                   obj->layer->evas->hinting);
9404                          }
9405                     }
9406                }
9407           }
9408      }
9409    _evas_textblock_invalidate_all(o);
9410    _evas_textblock_changed(o, obj);
9411 }
9412
9413 /**
9414  * @}
9415  */
9416
9417 #ifdef HAVE_TESTS
9418 /* return EINA_FALSE on error, used in unit_testing */
9419 EAPI Eina_Bool
9420 _evas_textblock_check_item_node_link(Evas_Object *obj)
9421 {
9422    Evas_Object_Textblock *o;
9423    Evas_Object_Textblock_Paragraph *par;
9424    Evas_Object_Textblock_Line *ln;
9425    Evas_Object_Textblock_Item *it;
9426
9427    o = (Evas_Object_Textblock *)(obj->object_data);
9428    if (!o) return EINA_FALSE;
9429
9430    if (!o->formatted.valid) _relayout(obj);
9431
9432    EINA_INLIST_FOREACH(o->paragraphs, par)
9433      {
9434         EINA_INLIST_FOREACH(par->lines, ln)
9435           {
9436              EINA_INLIST_FOREACH(ln->items, it)
9437                {
9438                   if (it->text_node != par->text_node)
9439                      return EINA_FALSE;
9440                }
9441           }
9442      }
9443    return EINA_TRUE;
9444 }
9445
9446 EAPI int
9447 _evas_textblock_format_offset_get(const Evas_Object_Textblock_Node_Format *n)
9448 {
9449    return n->offset;
9450 }
9451 #endif
9452
9453 #if 0
9454 /* Good for debugging */
9455 void
9456 pfnode(Evas_Object_Textblock_Node_Format *n)
9457 {
9458    printf("Format Node: %p\n", n);
9459    printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
9460    printf("text_node = %p, offset = %u, visible = %d\n", n->text_node, n->offset, n->visible);
9461    printf("'%s'\n", eina_strbuf_string_get(n->format));
9462 }
9463
9464 void
9465 ptnode(Evas_Object_Textblock_Node_Text *n)
9466 {
9467    printf("Text Node: %p\n", n);
9468    printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
9469    printf("format_node = %p\n", n->format_node);
9470    printf("'%ls'\n", eina_ustrbuf_string_get(n->unicode));
9471 }
9472
9473 void
9474 pitem(Evas_Object_Textblock_Item *it)
9475 {
9476    Evas_Object_Textblock_Text_Item *ti;
9477    Evas_Object_Textblock_Format_Item *fi;
9478    printf("Item: %p\n", it);
9479    printf("Type: %s (%d)\n", (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
9480          "TEXT" : "FORMAT", it->type);
9481    printf("Text pos: %d Visual pos: %d\n", it->text_pos,
9482 #ifdef BIDI_SUPPORT
9483          it->visual_pos
9484 #else
9485          it->text_pos
9486 #endif
9487          );
9488    printf("Coords: x = %d w = %d adv = %d\n", (int) it->x, (int) it->w,
9489          (int) it->adv);
9490    if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
9491      {
9492         ti = _ITEM_TEXT(it);
9493         printf("Text: '%*ls'\n", ti->text_props.text_len, GET_ITEM_TEXT(ti));
9494      }
9495    else
9496      {
9497         fi = _ITEM_FORMAT(it);
9498         printf("Format: '%s'\n", fi->item);
9499      }
9500 }
9501
9502 void
9503 ppar(Evas_Object_Textblock_Paragraph *par)
9504 {
9505    Evas_Object_Textblock_Item *it;
9506    Eina_List *i;
9507    EINA_LIST_FOREACH(par->logical_items, i, it)
9508      {
9509         printf("***********************\n");
9510         pitem(it);
9511      }
9512 }
9513
9514 #endif
9515