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