big patch from Samsung SAIT (Advanced research group) for async multi-frame
[framework/uifw/evas.git] / src / lib / canvas / evas_object_textblock.c
1 /*
2  * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
3  */
4
5 #include "evas_common.h"
6 #include "evas_private.h"
7
8 /* save typing */
9 #define ENFN obj->layer->evas->engine.func
10 #define ENDT obj->layer->evas->engine.data.output
11
12 /* private magic number for textblock objects */
13 static const char o_type[] = "textblock";
14
15 /* private struct for textblock object internal data */
16 typedef struct _Evas_Object_Textblock             Evas_Object_Textblock;
17 typedef struct _Evas_Object_Style_Tag             Evas_Object_Style_Tag;
18 typedef struct _Evas_Object_Textblock_Node        Evas_Object_Textblock_Node;
19 typedef struct _Evas_Object_Textblock_Line        Evas_Object_Textblock_Line;
20 typedef struct _Evas_Object_Textblock_Item        Evas_Object_Textblock_Item;
21 typedef struct _Evas_Object_Textblock_Format_Item Evas_Object_Textblock_Format_Item;
22 typedef struct _Evas_Object_Textblock_Format      Evas_Object_Textblock_Format;
23
24 /* the current state of the formatting */
25
26 #define  NODE_TEXT   0
27 #define  NODE_FORMAT 1
28
29 struct _Evas_Object_Style_Tag
30 {
31    EINA_INLIST;
32    char *tag;
33    char *replace;
34    size_t tag_len;
35    size_t replace_len;
36 };
37
38 struct _Evas_Object_Textblock_Node
39 {
40    EINA_INLIST;
41    Eina_Strbuf *text;
42    int          type;
43 };
44
45 struct _Evas_Object_Textblock_Line
46 {
47    EINA_INLIST;
48    Evas_Object_Textblock_Item        *items;
49    Evas_Object_Textblock_Format_Item *format_items;
50    int                                x, y, w, h;
51    int                                baseline;
52    int                                line_no;
53 };
54
55 struct _Evas_Object_Textblock_Item
56 {
57    EINA_INLIST;
58    char                         *text;
59    Evas_Object_Textblock_Format *format;
60    Evas_Object_Textblock_Node   *source_node;
61    int                           x, w, h;
62    int                           inset, baseline;
63    int                           source_pos;
64    unsigned char                 type;
65 };
66
67 struct _Evas_Object_Textblock_Format_Item
68 {
69    EINA_INLIST;
70    const char                   *item;
71    Evas_Object_Textblock_Node   *source_node;
72    int                           x, w, h, y, ascent, descent;
73    unsigned char                 vsize : 2;
74    unsigned char                 size : 2;
75    unsigned char                 formatme : 1;
76    unsigned char                 ___padding___ : 3;
77 };
78
79 struct _Evas_Object_Textblock_Format
80 {
81    int                  ref;
82    double               halign;
83    double               valign;
84    struct {
85       const char       *name;
86       const char       *source;
87       const char       *fallbacks;
88       int               size;
89       void             *font;
90    } font;
91    struct {
92       struct {
93          unsigned char  r, g, b, a;
94       } normal, underline, underline2, outline, shadow, glow, glow2, backing,
95         strikethrough;
96    } color;
97    struct {
98       int               l, r;
99    } margin;
100    int                  tabstops;
101    int                  linesize;
102    double               linerelsize;
103    int                  linegap;
104    double               linerelgap;
105    unsigned char        style;
106    unsigned char        wrap_word : 1;
107    unsigned char        wrap_char : 1;
108    unsigned char        underline : 1;
109    unsigned char        underline2 : 1;
110    unsigned char        strikethrough : 1;
111    unsigned char        backing : 1;
112 };
113
114 struct _Evas_Textblock_Style
115 {
116    char                  *style_text;
117    char                  *default_tag;
118    Evas_Object_Style_Tag *tags;
119    Eina_List             *objects;
120    unsigned char          delete_me : 1;
121 };
122
123 struct _Evas_Textblock_Cursor
124 {
125    Evas_Object                *obj;
126    int                         pos;
127    Evas_Object_Textblock_Node *node;
128    Eina_Bool                   eol : 1;
129 };
130
131 struct _Evas_Object_Textblock
132 {
133    DATA32                       magic;
134    Evas_Textblock_Style        *style;
135    Evas_Textblock_Cursor       *cursor;
136    Eina_List                   *cursors;
137    Evas_Object_Textblock_Node  *nodes;
138    Evas_Object_Textblock_Line  *lines;
139    int                          last_w;
140    struct {
141       int                       l, r, t, b;
142    } style_pad;
143    char                        *markup_text;
144    void                        *engine_data;
145    const char                  *repch;
146    struct {
147       int                       w, h;
148       unsigned char             valid : 1;
149    } formatted, native;
150    unsigned char                redraw : 1;
151    unsigned char                changed : 1;
152 };
153
154 /* private methods for textblock objects */
155 static void evas_object_textblock_init(Evas_Object *obj);
156 static void *evas_object_textblock_new(void);
157 static void evas_object_textblock_render(Evas_Object *obj, void *output, void *context, void *surface, int x, int y);
158 static void evas_object_textblock_free(Evas_Object *obj);
159 static void evas_object_textblock_render_pre(Evas_Object *obj);
160 static void evas_object_textblock_render_post(Evas_Object *obj);
161
162 static unsigned int evas_object_textblock_id_get(Evas_Object *obj);
163 static unsigned int evas_object_textblock_visual_id_get(Evas_Object *obj);
164 static void *evas_object_textblock_engine_data_get(Evas_Object *obj);
165
166 static int evas_object_textblock_is_opaque(Evas_Object *obj);
167 static int evas_object_textblock_was_opaque(Evas_Object *obj);
168
169 static void evas_object_textblock_coords_recalc(Evas_Object *obj);
170
171 static void evas_object_textblock_scale_update(Evas_Object *obj);
172
173 static const Evas_Object_Func object_func =
174 {
175    /* methods (compulsory) */
176    evas_object_textblock_free,
177      evas_object_textblock_render,
178      evas_object_textblock_render_pre,
179      evas_object_textblock_render_post,
180      evas_object_textblock_id_get,
181      evas_object_textblock_visual_id_get,
182      evas_object_textblock_engine_data_get,
183    /* these are optional. NULL = nothing */
184      NULL,
185      NULL,
186      NULL,
187      NULL,
188      evas_object_textblock_is_opaque,
189      evas_object_textblock_was_opaque,
190      NULL,
191      NULL,
192      evas_object_textblock_coords_recalc,
193      evas_object_textblock_scale_update,
194      NULL,
195      NULL,
196      NULL
197 };
198
199 /* the actual api call to add a textblock */
200
201 #define TB_HEAD() \
202    Evas_Object_Textblock *o; \
203    MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ); \
204    return; \
205    MAGIC_CHECK_END(); \
206    o = (Evas_Object_Textblock *)(obj->object_data); \
207    MAGIC_CHECK(o, Evas_Object_Textblock, MAGIC_OBJ_TEXTBLOCK); \
208    return; \
209    MAGIC_CHECK_END();
210
211 #define TB_HEAD_RETURN(x) \
212    Evas_Object_Textblock *o; \
213    MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ); \
214    return (x); \
215    MAGIC_CHECK_END(); \
216    o = (Evas_Object_Textblock *)(obj->object_data); \
217    MAGIC_CHECK(o, Evas_Object_Textblock, MAGIC_OBJ_TEXTBLOCK); \
218    return (x); \
219    MAGIC_CHECK_END();
220
221
222 /**
223  * @addtogroup Evas_Object_Textblock
224  * @{
225  */
226
227 /* styles */
228 static void
229 _style_clear(Evas_Textblock_Style *ts)
230 {
231    if (ts->style_text) free(ts->style_text);
232    if (ts->default_tag) free(ts->default_tag);
233    while (ts->tags)
234      {
235         Evas_Object_Style_Tag *tag;
236
237         tag = (Evas_Object_Style_Tag *)ts->tags;
238         ts->tags = (Evas_Object_Style_Tag *)eina_inlist_remove(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
239         free(tag->tag);
240         free(tag->replace);
241         free(tag);
242      }
243    ts->style_text = NULL;
244    ts->default_tag = NULL;
245    ts->tags = NULL;
246 }
247
248 static inline const char *
249 _style_match_replace(Evas_Textblock_Style *ts, const char *s, size_t replace_len, size_t *tag_len)
250 {
251    Evas_Object_Style_Tag *tag;
252
253    EINA_INLIST_FOREACH(ts->tags, tag)
254      {
255         if (tag->replace_len != replace_len) continue;
256         if (!strcmp(tag->replace, s))
257           {
258              *tag_len = tag->tag_len;
259              return tag->tag;
260           }
261      }
262    *tag_len = 0;
263    return NULL;
264 }
265
266 static inline const char *
267 _style_match_tag(Evas_Textblock_Style *ts, const char *s, size_t tag_len, size_t *replace_len)
268 {
269    Evas_Object_Style_Tag *tag;
270
271    EINA_INLIST_FOREACH(ts->tags, tag)
272      {
273         if (tag->tag_len != tag_len) continue;
274         if (!strcmp(tag->tag, s))
275           {
276              *replace_len = tag->replace_len;
277              return tag->replace;
278           }
279      }
280    *replace_len = 0;
281    return NULL;
282 }
283
284 static void
285 _nodes_clear(const Evas_Object *obj)
286 {
287    Evas_Object_Textblock *o;
288
289    o = (Evas_Object_Textblock *)(obj->object_data);
290    while (o->nodes)
291      {
292         Evas_Object_Textblock_Node *n;
293
294         n = (Evas_Object_Textblock_Node *)o->nodes;
295         o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_remove(EINA_INLIST_GET(o->nodes), EINA_INLIST_GET(n));
296         if (n->text) eina_strbuf_free(n->text);
297         free(n);
298      }
299 }
300
301 static void
302 _format_free(const Evas_Object *obj, Evas_Object_Textblock_Format *fmt)
303 {
304    fmt->ref--;
305    if (fmt->ref > 0) return;
306    if (fmt->font.name) eina_stringshare_del(fmt->font.name);
307    if (fmt->font.fallbacks) eina_stringshare_del(fmt->font.fallbacks);
308    if (fmt->font.source) eina_stringshare_del(fmt->font.source);
309    evas_font_free(obj->layer->evas, fmt->font.font);
310    free(fmt);
311 }
312
313 static void
314 _line_free(const Evas_Object *obj, Evas_Object_Textblock_Line *ln)
315 {
316    while (ln->items)
317      {
318         Evas_Object_Textblock_Item *it;
319
320         it = (Evas_Object_Textblock_Item *)ln->items;
321         ln->items = (Evas_Object_Textblock_Item *)eina_inlist_remove(EINA_INLIST_GET(ln->items), EINA_INLIST_GET(ln->items));
322         if (it->text) free(it->text);
323         _format_free(obj, it->format);
324         free(it);
325      }
326    while (ln->format_items)
327      {
328         Evas_Object_Textblock_Format_Item *fi;
329
330         fi = (Evas_Object_Textblock_Format_Item *)ln->format_items;
331         ln->format_items = (Evas_Object_Textblock_Format_Item *)eina_inlist_remove(EINA_INLIST_GET(ln->format_items),
332                                                                                    EINA_INLIST_GET(ln->format_items));
333         if (fi->item) eina_stringshare_del(fi->item);
334         free(fi);
335      }
336    if (ln) free(ln);
337 }
338
339 static void
340 _lines_clear(const Evas_Object *obj, Evas_Object_Textblock_Line *lines)
341 {
342    while (lines)
343      {
344         Evas_Object_Textblock_Line *ln;
345
346         ln = (Evas_Object_Textblock_Line *)lines;
347         lines = (Evas_Object_Textblock_Line *)eina_inlist_remove(EINA_INLIST_GET(lines), EINA_INLIST_GET(ln));
348         _line_free(obj, ln);
349      }
350 }
351
352 static void
353 _nodes_adjacent_merge(const Evas_Object *obj, Evas_Object_Textblock_Node *n1)
354 {
355    Evas_Object_Textblock *o;
356    Evas_Object_Textblock_Node *n0, *n2;
357    Eina_List *l;
358    Evas_Textblock_Cursor *data;
359    int plen;
360
361    if (n1->type != NODE_TEXT) return;
362    o = (Evas_Object_Textblock *)(obj->object_data);
363    n0 = (Evas_Object_Textblock_Node *)(EINA_INLIST_GET(n1))->prev;
364    n2 = (Evas_Object_Textblock_Node *)(EINA_INLIST_GET(n1))->next;
365    if ((n0) && (n0->type == NODE_TEXT))
366      {
367         plen = eina_strbuf_length_get(n0->text);
368         eina_strbuf_append_length(n0->text, eina_strbuf_string_get(n1->text),
369                                   eina_strbuf_length_get(n1->text));
370         o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_remove
371           (EINA_INLIST_GET(o->nodes), EINA_INLIST_GET(n1));
372 //      (EINA_INLIST_GET(n0))->next = EINA_INLIST_GET(n2);
373 //      if (n2) (EINA_INLIST_GET(n2))->prev = EINA_INLIST_GET(n0);
374         // fix any cursors in n1
375         if (n1 == o->cursor->node)
376           {
377              o->cursor->node = n0;
378              o->cursor->pos += plen;
379           }
380         EINA_LIST_FOREACH(o->cursors, l, data)
381           {
382              if (n1 == data->node)
383                {
384                   data->node = n0;
385                   data->pos += plen;
386                }
387           }
388         if (n1->text) eina_strbuf_free(n1->text);
389         free(n1);
390         n1 = n0;
391         n2 = (Evas_Object_Textblock_Node *)(EINA_INLIST_GET(n1))->next;
392      }
393    if ((n2) && (n2->type == NODE_TEXT))
394      {
395         n0 = n1;
396         n1 = n2;
397         n2 = (Evas_Object_Textblock_Node *)(EINA_INLIST_GET(n1))->next;
398         plen = eina_strbuf_length_get(n0->text);
399         eina_strbuf_append_length(n0->text, eina_strbuf_string_get(n1->text),
400                                   eina_strbuf_length_get(n1->text));
401         o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_remove
402           (EINA_INLIST_GET(o->nodes), EINA_INLIST_GET(n1));
403 //      (EINA_INLIST_GET(n0))->next = EINA_INLIST_GET(n2);
404 //      if (n2) (EINA_INLIST_GET(n2))->prev = EINA_INLIST_GET(n0);
405         // fix any cursors in n1
406         if (n1 == o->cursor->node)
407           {
408              o->cursor->node = n0;
409              o->cursor->pos += plen;
410           }
411         EINA_LIST_FOREACH(o->cursors, l, data)
412           {
413              if (n1 == data->node)
414                {
415                   data->node = n0;
416                   data->pos += plen;
417                }
418           }
419         if (n1->text) eina_strbuf_free(n1->text);
420         free(n1);
421      }
422 }
423
424 /* table of html escapes (that i can find) this should be ordered with the
425  * most common first as it's a linear search to match - no hash for this.
426  *
427  * these are stored as one large string and one additional array that
428  * contains the offsets to the tokens for space efficiency.
429  */
430 static const char escape_strings[] =
431         /* most common escaped stuff */
432         " \0"          "\x20\0" /* NOTE: this here to avoid escaping to &nbsp */
433         " \0"     "\x20\0" /* NOTE: we allow nsbp's to break as we map early - maybe map to ascii 0x01 and then make the rendering code think 0x01 -> 0x20 */
434         ""\0"     "\x22\0"
435         "&\0"      "\x26\0"
436         "<\0"       "\x3c\0"
437         ">\0"       "\x3e\0"
438         /* all the rest */
439         "¡\0"    "\xc2\xa1\0"
440         "¢\0"     "\xc2\xa2\0"
441         "£\0"    "\xc2\xa3\0"
442         "¤\0"   "\xc2\xa4\0"
443         "¥\0"      "\xc2\xa5\0"
444         "¦\0"   "\xc2\xa6\0"
445         "§\0"     "\xc2\xa7\0"
446         "¨\0"      "\xc2\xa8\0"
447         "©\0"     "\xc2\xa9\0"
448         "ª\0"     "\xc2\xaa\0"
449         "«\0"    "\xc2\xab\0"
450         "¬\0"      "\xc2\xac\0"
451         "®\0"      "\xc2\xae\0"
452         "¯\0"     "\xc2\xaf\0"
453         "°\0"      "\xc2\xb0\0"
454         "±\0"   "\xc2\xb1\0"
455         "²\0"     "\xc2\xb2\0"
456         "³\0"     "\xc2\xb3\0"
457         "´\0"    "\xc2\xb4\0"
458         "µ\0"    "\xc2\xb5\0"
459         "¶\0"     "\xc2\xb6\0"
460         "·\0"   "\xc2\xb7\0"
461         "¸\0"    "\xc2\xb8\0"
462         "¹\0"     "\xc2\xb9\0"
463         "º\0"     "\xc2\xba\0"
464         "»\0"    "\xc2\xbb\0"
465         "¼\0"   "\xc2\xbc\0"
466         "½\0"   "\xc2\xbd\0"
467         "¾\0"   "\xc2\xbe\0"
468         "¿\0"   "\xc2\xbf\0"
469         "À\0"   "\xc3\x80\0"
470         "Á\0"   "\xc3\x81\0"
471         "Â\0"    "\xc3\x82\0"
472         "Ã\0"   "\xc3\x83\0"
473         "Ä\0"     "\xc3\x84\0"
474         "Å\0"    "\xc3\x85\0"
475         "&Aelig;\0"    "\xc3\x86\0"
476         "Ç\0"   "\xc3\x87\0"
477         "È\0"   "\xc3\x88\0"
478         "É\0"   "\xc3\x89\0"
479         "Ê\0"    "\xc3\x8a\0"
480         "Ë\0"     "\xc3\x8b\0"
481         "È\0"   "\xc3\x8c\0"
482         "É\0"   "\xc3\x8d\0"
483         "Î\0"    "\xc3\x8e\0"
484         "Ï\0"     "\xc3\x8f\0"
485         "&Eth;\0"      "\xc3\x90\0"
486         "Ñ\0"   "\xc3\x91\0"
487         "Ò\0"   "\xc3\x92\0"
488         "Ó\0"   "\xc3\x93\0"
489         "Ô\0"    "\xc3\x94\0"
490         "Õ\0"   "\xc3\x95\0"
491         "Ö\0"     "\xc3\x96\0"
492         "×\0"    "\xc3\x97\0"
493         "Ø\0"   "\xc3\x98\0"
494         "Ù\0"   "\xc3\x99\0"
495         "Ú\0"   "\xc3\x9a\0"
496         "Û\0"    "\xc3\x9b\0"
497         "Ý\0"   "\xc3\x9d\0"
498         "&Thorn;\0"    "\xc3\x9e\0"
499         "ß\0"    "\xc3\x9f\0"
500         "à\0"   "\xc3\xa0\0"
501         "á\0"   "\xc3\xa1\0"
502         "â\0"    "\xc3\xa2\0"
503         "ã\0"   "\xc3\xa3\0"
504         "ä\0"     "\xc3\xa4\0"
505         "å\0"    "\xc3\xa5\0"
506         "æ\0"    "\xc3\xa6\0"
507         "ç\0"   "\xc3\xa7\0"
508         "è\0"   "\xc3\xa8\0"
509         "é\0"   "\xc3\xa9\0"
510         "ê\0"    "\xc3\xaa\0"
511         "ë\0"     "\xc3\xab\0"
512         "ì\0"   "\xc3\xac\0"
513         "í\0"   "\xc3\xad\0"
514         "î\0"    "\xc3\xae\0"
515         "ï\0"     "\xc3\xaf\0"
516         "ð\0"      "\xc3\xb0\0"
517         "ñ\0"   "\xc3\xb1\0"
518         "ò\0"   "\xc3\xb2\0"
519         "ó\0"   "\xc3\xb3\0"
520         "ô\0"    "\xc3\xb4\0"
521         "õ\0"   "\xc3\xb5\0"
522         "ö\0"     "\xc3\xb6\0"
523         "÷\0"   "\xc3\xb7\0"
524         "ø\0"   "\xc3\xb8\0"
525         "ù\0"   "\xc3\xb9\0"
526         "ú\0"   "\xc3\xba\0"
527         "û\0"    "\xc3\xbb\0"
528         "ü\0"     "\xc3\xbc\0"
529         "ý\0"   "\xc3\xbd\0"
530         "þ\0"    "\xc3\xbe\0"
531         "ÿ\0"     "\xc3\xbf\0"
532         "α\0"    "\xce\x91\0"
533         "β\0"     "\xce\x92\0"
534         "γ\0"    "\xce\x93\0"
535         "δ\0"    "\xce\x94\0"
536         "ε\0"  "\xce\x95\0"
537         "ζ\0"     "\xce\x96\0"
538         "η\0"      "\xce\x97\0"
539         "θ\0"    "\xce\x98\0"
540         "ι\0"     "\xce\x99\0"
541         "κ\0"    "\xce\x9a\0"
542         "λ\0"   "\xce\x9b\0"
543         "μ\0"       "\xce\x9c\0"
544         "ν\0"       "\xce\x9d\0"
545         "ξ\0"       "\xce\x9e\0"
546         "ο\0"  "\xce\x9f\0"
547         "π\0"       "\xce\xa0\0"
548         "ρ\0"      "\xce\xa1\0"
549         "σ\0"    "\xce\xa3\0"
550         "τ\0"      "\xce\xa4\0"
551         "υ\0"  "\xce\xa5\0"
552         "φ\0"      "\xce\xa6\0"
553         "χ\0"      "\xce\xa7\0"
554         "ψ\0"      "\xce\xa8\0"
555         "ω\0"    "\xce\xa9\0"
556         "…\0"   "\xe2\x80\xa6\0"
557         "€\0"     "\xe2\x82\xac\0"
558         "←\0"     "\xe2\x86\x90\0"
559         "↑\0"     "\xe2\x86\x91\0"
560         "→\0"     "\xe2\x86\x92\0"
561         "↓\0"     "\xe2\x86\x93\0"
562         "↔\0"     "\xe2\x86\x94\0"
563         "←\0"     "\xe2\x87\x90\0"
564         "→\0"     "\xe2\x87\x92\0"
565         "∀\0"   "\xe2\x88\x80\0"
566         "∃\0"    "\xe2\x88\x83\0"
567         "∇\0"    "\xe2\x88\x87\0"
568         "∏\0"     "\xe2\x88\x8f\0"
569         "∑\0"      "\xe2\x88\x91\0"
570         "∧\0"      "\xe2\x88\xa7\0"
571         "∨\0"       "\xe2\x88\xa8\0"
572         "∫\0"      "\xe2\x88\xab\0"
573         "≠\0"       "\xe2\x89\xa0\0"
574         "≡\0"    "\xe2\x89\xa1\0"
575         "⊕\0"    "\xe2\x8a\x95\0"
576         "⊥\0"     "\xe2\x8a\xa5\0"
577 ;
578
579
580 static int
581 _is_white(int c)
582 {
583    /*
584     * unicode list of whitespace chars
585     *
586     * 0009..000D <control-0009>..<control-000D>
587     * 0020 SPACE
588     * 0085 <control-0085>
589     * 00A0 NO-BREAK SPACE
590     * 1680 OGHAM SPACE MARK
591     * 180E MONGOLIAN VOWEL SEPARATOR
592     * 2000..200A EN QUAD..HAIR SPACE
593     * 2028 LINE SEPARATOR
594     * 2029 PARAGRAPH SEPARATOR
595     * 202F NARROW NO-BREAK SPACE
596     * 205F MEDIUM MATHEMATICAL SPACE
597     * 3000 IDEOGRAPHIC SPACE
598     */
599    if (
600        (c == 0x20) ||
601        ((c >= 0x9) && (c <= 0xd)) ||
602        (c == 0x85) ||
603        (c == 0xa0) ||
604        (c == 0x1680) ||
605        (c == 0x180e) ||
606        ((c >= 0x2000) && (c <= 0x200a)) ||
607        (c == 0x2028) ||
608        (c == 0x2029) ||
609        (c == 0x202f) ||
610        (c == 0x205f) ||
611        (c == 0x3000)
612        )
613      return 1;
614    return 0;
615 }
616
617 static char *
618 _clean_white(int clean_start, int clean_end, char *str)
619 {
620    char *p, *p2, *str2 = NULL;
621    int white, pwhite, start, ok;
622
623    return str;
624    str2 = malloc(strlen(str) + 2);
625    p = str;
626    p2 = str2;
627    white = 0;
628    start = 1;
629    ok = 1;
630    while (*p != 0)
631      {
632         pwhite = white;
633         if (isspace(*p) || _is_white(*p)) white = 1;
634         else white = 0;
635         if ((pwhite) && (white)) ok = 0;
636         else
637           {
638              if (!clean_start)
639                {
640                   if ((start) && (pwhite) && (!white))
641                     {
642 //                     *p2 = ' ';
643 //                     p2++;
644                     }
645                }
646              ok = 1;
647              if (!white) start = 0;
648           }
649         if (clean_start)
650           {
651              if ((start) && (ok)) ok = 0;
652           }
653         if (ok)
654           {
655              *p2 = *p;
656              p2++;
657           }
658         p++;
659      }
660    *p2 = 0;
661    if (clean_end)
662      {
663         while (p2 > str2)
664           {
665              p2--;
666              if (!(isspace(*p2) || _is_white(*p2))) break;
667              *p2 = 0;
668           }
669      }
670    free(str);
671    return str2;
672 }
673
674 static void
675 _append_text_run(Evas_Object_Textblock *o, char *s, char *p)
676 {
677    if ((s) && (p > s))
678      {
679         char *ts;
680
681         ts = alloca(p - s + 1);
682         strncpy(ts, s, p - s);
683         ts[p - s] = 0;
684         ts = _clean_white(0, 0, ts);
685         evas_textblock_cursor_text_append(o->cursor, ts);
686      }
687 }
688
689 static void
690 _prepend_text_run(Evas_Object_Textblock *o, char *s, char *p)
691 {
692    if ((s) && (p > s))
693      {
694         char *ts;
695
696         ts = alloca(p - s + 1);
697         strncpy(ts, s, p - s);
698         ts[p - s] = 0;
699         ts = _clean_white(0, 0, ts);
700         evas_textblock_cursor_text_prepend(o->cursor, ts);
701      }
702 }
703
704
705 static int
706 _hex_string_get(char ch)
707 {
708    if ((ch >= '0') && (ch <= '9')) return (ch - '0');
709    else if ((ch >= 'A') && (ch <= 'F')) return (ch - 'A' + 10);
710    else if ((ch >= 'a') && (ch <= 'f')) return (ch - 'a' + 10);
711    return 0;
712 }
713
714 static void
715 _format_color_parse(const char *str, unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a)
716 {
717    int slen;
718
719    slen = strlen(str);
720    *r = *g = *b = *a = 0;
721
722    if (slen == 7) /* #RRGGBB */
723      {
724         *r = (_hex_string_get(str[1]) << 4) | (_hex_string_get(str[2]));
725         *g = (_hex_string_get(str[3]) << 4) | (_hex_string_get(str[4]));
726         *b = (_hex_string_get(str[5]) << 4) | (_hex_string_get(str[6]));
727         *a = 0xff;
728      }
729    else if (slen == 9) /* #RRGGBBAA */
730      {
731         *r = (_hex_string_get(str[1]) << 4) | (_hex_string_get(str[2]));
732         *g = (_hex_string_get(str[3]) << 4) | (_hex_string_get(str[4]));
733         *b = (_hex_string_get(str[5]) << 4) | (_hex_string_get(str[6]));
734         *a = (_hex_string_get(str[7]) << 4) | (_hex_string_get(str[8]));
735      }
736    else if (slen == 4) /* #RGB */
737      {
738         *r = _hex_string_get(str[1]);
739         *r = (*r << 4) | *r;
740         *g = _hex_string_get(str[2]);
741         *g = (*g << 4) | *g;
742         *b = _hex_string_get(str[3]);
743         *b = (*b << 4) | *b;
744         *a = 0xff;
745      }
746    else if (slen == 5) /* #RGBA */
747      {
748         *r = _hex_string_get(str[1]);
749         *r = (*r << 4) | *r;
750         *g = _hex_string_get(str[2]);
751         *g = (*g << 4) | *g;
752         *b = _hex_string_get(str[3]);
753         *b = (*b << 4) | *b;
754         *a = _hex_string_get(str[4]);
755         *a = (*a << 4) | *a;
756      }
757    *r = (*r * *a) / 255;
758    *g = (*g * *a) / 255;
759    *b = (*b * *a) / 255;
760 }
761
762 static const char *fontstr = NULL;
763 static const char *font_fallbacksstr = NULL;
764 static const char *font_sizestr = NULL;
765 static const char *font_sourcestr = NULL;
766 static const char *colorstr = NULL;
767 static const char *underline_colorstr = NULL;
768 static const char *underline2_colorstr = NULL;
769 static const char *outline_colorstr = NULL;
770 static const char *shadow_colorstr = NULL;
771 static const char *glow_colorstr = NULL;
772 static const char *glow2_colorstr = NULL;
773 static const char *backing_colorstr = NULL;
774 static const char *strikethrough_colorstr = NULL;
775 static const char *alignstr = NULL;
776 static const char *valignstr = NULL;
777 static const char *wrapstr = NULL;
778 static const char *left_marginstr = NULL;
779 static const char *right_marginstr = NULL;
780 static const char *underlinestr = NULL;
781 static const char *strikethroughstr = NULL;
782 static const char *backingstr = NULL;
783 static const char *stylestr = NULL;
784 static const char *tabstopsstr = NULL;
785 static const char *linesizestr = NULL;
786 static const char *linerelsizestr = NULL;
787 static const char *linegapstr = NULL;
788 static const char *linerelgapstr = NULL;
789 static const char *itemstr = NULL;
790
791 static void
792 _format_command_init(void)
793 {
794    if (fontstr) return;
795    fontstr = eina_stringshare_add("font");
796    font_fallbacksstr = eina_stringshare_add("font_fallbacks");
797    font_sizestr = eina_stringshare_add("font_size");
798    font_sourcestr = eina_stringshare_add("font_source");
799    colorstr = eina_stringshare_add("color");
800    underline_colorstr = eina_stringshare_add("underline_color");
801    underline2_colorstr = eina_stringshare_add("underline2_color");
802    outline_colorstr = eina_stringshare_add("outline_color");
803    shadow_colorstr = eina_stringshare_add("shadow_color");
804    glow_colorstr = eina_stringshare_add("glow_color");
805    glow2_colorstr = eina_stringshare_add("glow2_color");
806    backing_colorstr = eina_stringshare_add("backing_color");
807    strikethrough_colorstr = eina_stringshare_add("strikethrough_color");
808    alignstr = eina_stringshare_add("align");
809    valignstr = eina_stringshare_add("valign");
810    wrapstr = eina_stringshare_add("wrap");
811    left_marginstr = eina_stringshare_add("left_margin");
812    right_marginstr = eina_stringshare_add("right_margin");
813    underlinestr = eina_stringshare_add("underline");
814    strikethroughstr = eina_stringshare_add("strikethrough");
815    backingstr = eina_stringshare_add("backing");
816    stylestr = eina_stringshare_add("style");
817    tabstopsstr = eina_stringshare_add("tabstops");
818    linesizestr = eina_stringshare_add("linesize");
819    linerelsizestr = eina_stringshare_add("linerelsize");
820    linegapstr = eina_stringshare_add("linegap");
821    linerelgapstr = eina_stringshare_add("linerelgap");
822    itemstr = eina_stringshare_add("item");
823 }
824
825 static void
826 _format_command_shutdown(void)
827 {
828    return;
829    eina_stringshare_del(fontstr);
830    eina_stringshare_del(font_fallbacksstr);
831    eina_stringshare_del(font_sizestr);
832    eina_stringshare_del(font_sourcestr);
833    eina_stringshare_del(colorstr);
834    eina_stringshare_del(underline_colorstr);
835    eina_stringshare_del(underline2_colorstr);
836    eina_stringshare_del(outline_colorstr);
837    eina_stringshare_del(shadow_colorstr);
838    eina_stringshare_del(glow_colorstr);
839    eina_stringshare_del(glow2_colorstr);
840    eina_stringshare_del(backing_colorstr);
841    eina_stringshare_del(strikethrough_colorstr);
842    eina_stringshare_del(alignstr);
843    eina_stringshare_del(valignstr);
844    eina_stringshare_del(wrapstr);
845    eina_stringshare_del(left_marginstr);
846    eina_stringshare_del(right_marginstr);
847    eina_stringshare_del(underlinestr);
848    eina_stringshare_del(strikethroughstr);
849    eina_stringshare_del(backingstr);
850    eina_stringshare_del(stylestr);
851    eina_stringshare_del(tabstopsstr);
852    eina_stringshare_del(linesizestr);
853    eina_stringshare_del(linerelsizestr);
854    eina_stringshare_del(linegapstr);
855    eina_stringshare_del(linerelgapstr);
856    eina_stringshare_del(itemstr);
857 }
858
859 static void
860 _format_clean_param(char *dst, const char *src)
861 {
862    const char *ss;
863    char *ds;
864
865    ds = dst;
866    for (ss = src; *ss; ss++, ds++)
867      {
868         if ((*ss == '\\') && *(ss + 1)) ss++;
869         *ds = *ss;
870      }
871    *ds = 0;
872 }
873
874 static void
875 _format_command(Evas_Object *obj, Evas_Object_Textblock_Format *fmt, const char *cmd, const char *param)
876 {
877    int new_font = 0;
878    char *tmp_param;
879
880    tmp_param = alloca(strlen(param) + 1);
881
882    _format_clean_param(tmp_param, param);
883    if (cmd == fontstr)
884      {
885         if ((!fmt->font.name) ||
886             ((fmt->font.name) && (strcmp(fmt->font.name, tmp_param))))
887           {
888              if (fmt->font.name) eina_stringshare_del(fmt->font.name);
889              fmt->font.name = eina_stringshare_add(tmp_param);
890              new_font = 1;
891           }
892      }
893    else if (cmd == font_fallbacksstr)
894      {
895         if ((!fmt->font.fallbacks) ||
896             ((fmt->font.fallbacks) && (strcmp(fmt->font.fallbacks, tmp_param))))
897           {
898              /* policy - when we say "fallbacks" do we prepend and use prior
899               * fallbacks... or should we replace. for now we replace
900               */
901              if (fmt->font.fallbacks) eina_stringshare_del(fmt->font.fallbacks);
902              fmt->font.fallbacks = eina_stringshare_add(tmp_param);
903              new_font = 1;
904           }
905      }
906    else if (cmd == font_sizestr)
907      {
908         int v;
909
910         v = atoi(tmp_param);
911         if (v != fmt->font.size)
912           {
913              fmt->font.size = v;
914              new_font = 1;
915           }
916      }
917    else if (cmd == font_sourcestr)
918      {
919         if ((!fmt->font.source) ||
920             ((fmt->font.source) && (strcmp(fmt->font.source, tmp_param))))
921           {
922              if (fmt->font.source) eina_stringshare_del(fmt->font.source);
923              fmt->font.source = eina_stringshare_add(tmp_param);
924              new_font = 1;
925           }
926      }
927    else if (cmd == colorstr)
928      _format_color_parse(tmp_param,
929                          &(fmt->color.normal.r), &(fmt->color.normal.g),
930                          &(fmt->color.normal.b), &(fmt->color.normal.a));
931    else if (cmd == underline_colorstr)
932      _format_color_parse(tmp_param,
933                          &(fmt->color.underline.r), &(fmt->color.underline.g),
934                          &(fmt->color.underline.b), &(fmt->color.underline.a));
935    else if (cmd == underline2_colorstr)
936      _format_color_parse(tmp_param,
937                          &(fmt->color.underline2.r), &(fmt->color.underline2.g),
938                          &(fmt->color.underline2.b), &(fmt->color.underline2.a));
939    else if (cmd == outline_colorstr)
940      _format_color_parse(tmp_param,
941                          &(fmt->color.outline.r), &(fmt->color.outline.g),
942                          &(fmt->color.outline.b), &(fmt->color.outline.a));
943    else if (cmd == shadow_colorstr)
944      _format_color_parse(tmp_param,
945                          &(fmt->color.shadow.r), &(fmt->color.shadow.g),
946                          &(fmt->color.shadow.b), &(fmt->color.shadow.a));
947    else if (cmd == glow_colorstr)
948      _format_color_parse(tmp_param,
949                          &(fmt->color.glow.r), &(fmt->color.glow.g),
950                          &(fmt->color.glow.b), &(fmt->color.glow.a));
951    else if (cmd == glow2_colorstr)
952      _format_color_parse(tmp_param,
953                          &(fmt->color.glow2.r), &(fmt->color.glow2.g),
954                          &(fmt->color.glow2.b), &(fmt->color.glow2.a));
955    else if (cmd == backing_colorstr)
956      _format_color_parse(tmp_param,
957                          &(fmt->color.backing.r), &(fmt->color.backing.g),
958                          &(fmt->color.backing.b), &(fmt->color.backing.a));
959    else if (cmd == strikethrough_colorstr)
960      _format_color_parse(tmp_param,
961                          &(fmt->color.strikethrough.r), &(fmt->color.strikethrough.g),
962                          &(fmt->color.strikethrough.b), &(fmt->color.strikethrough.a));
963    else if (cmd == alignstr)
964      {
965         if (!strcmp(tmp_param, "middle")) fmt->halign = 0.5;
966         else if (!strcmp(tmp_param, "center")) fmt->halign = 0.5;
967         else if (!strcmp(tmp_param, "left")) fmt->halign = 0.0;
968         else if (!strcmp(tmp_param, "right")) fmt->halign = 1.0;
969         else
970           {
971              char *endptr = NULL;
972              double val = strtod(tmp_param, &endptr);
973              if (endptr)
974                {
975                   while (*endptr && _is_white(*endptr))
976                     endptr++;
977                   if (*endptr == '%')
978                     val /= 100.0;
979                }
980              fmt->halign = val;;
981              if (fmt->halign < 0.0) fmt->halign = 0.0;
982              else if (fmt->halign > 1.0) fmt->halign = 1.0;
983           }
984      }
985    else if (cmd == valignstr)
986      {
987         if (!strcmp(tmp_param, "top")) fmt->valign = 0.0;
988         else if (!strcmp(tmp_param, "middle")) fmt->valign = 0.5;
989         else if (!strcmp(tmp_param, "center")) fmt->valign = 0.5;
990         else if (!strcmp(tmp_param, "bottom")) fmt->valign = 1.0;
991         else if (!strcmp(tmp_param, "baseline")) fmt->valign = -1.0;
992         else if (!strcmp(tmp_param, "base")) fmt->valign = -1.0;
993         else
994           {
995              char *endptr = NULL;
996              double val = strtod(tmp_param, &endptr);
997              if (endptr)
998                {
999                   while (*endptr && _is_white(*endptr))
1000                     endptr++;
1001                   if (*endptr == '%')
1002                     val /= 100.0;
1003                }
1004              fmt->valign = val;
1005              if (fmt->valign < 0.0) fmt->valign = 0.0;
1006              else if (fmt->valign > 1.0) fmt->valign = 1.0;
1007           }
1008      }
1009    else if (cmd == wrapstr)
1010      {
1011         if (!strcmp(tmp_param, "word"))
1012           {
1013              fmt->wrap_word = 1;
1014              fmt->wrap_char = 0;
1015           }
1016         else if (!strcmp(tmp_param, "char"))
1017           {
1018              fmt->wrap_word = 0;
1019              fmt->wrap_char = 1;
1020           }
1021         else
1022           {
1023              fmt->wrap_word = 0;
1024              fmt->wrap_char = 0;
1025           }
1026      }
1027    else if (cmd == left_marginstr)
1028      {
1029         if (!strcmp(tmp_param, "reset"))
1030           fmt->margin.l = 0;
1031         else
1032           {
1033              if (tmp_param[0] == '+')
1034                fmt->margin.l += atoi(&(tmp_param[1]));
1035              else if (tmp_param[0] == '-')
1036                fmt->margin.l -= atoi(&(tmp_param[1]));
1037              else
1038                fmt->margin.l = atoi(tmp_param);
1039              if (fmt->margin.l < 0) fmt->margin.l = 0;
1040           }
1041      }
1042    else if (cmd == right_marginstr)
1043      {
1044         if (!strcmp(tmp_param, "reset"))
1045           fmt->margin.r = 0;
1046         else
1047           {
1048              if (tmp_param[0] == '+')
1049                fmt->margin.r += atoi(&(tmp_param[1]));
1050              else if (tmp_param[0] == '-')
1051                fmt->margin.r -= atoi(&(tmp_param[1]));
1052              else
1053                fmt->margin.r = atoi(tmp_param);
1054              if (fmt->margin.r < 0) fmt->margin.r = 0;
1055           }
1056      }
1057    else if (cmd == underlinestr)
1058      {
1059         if (!strcmp(tmp_param, "off"))
1060           {
1061              fmt->underline = 0;
1062              fmt->underline2 = 0;
1063           }
1064         else if ((!strcmp(tmp_param, "on")) ||
1065                  (!strcmp(tmp_param, "single")))
1066           {
1067              fmt->underline = 1;
1068              fmt->underline2 = 0;
1069           }
1070         else if (!strcmp(tmp_param, "double"))
1071           {
1072              fmt->underline = 1;
1073              fmt->underline2 = 1;
1074           }
1075      }
1076    else if (cmd == strikethroughstr)
1077      {
1078         if (!strcmp(tmp_param, "off"))
1079           fmt->strikethrough = 0;
1080         else if (!strcmp(tmp_param, "on"))
1081           fmt->strikethrough = 1;
1082      }
1083    else if (cmd == backingstr)
1084      {
1085         if (!strcmp(tmp_param, "off"))
1086           fmt->backing = 0;
1087         else if (!strcmp(tmp_param, "on"))
1088           fmt->backing = 1;
1089      }
1090    else if (cmd == stylestr)
1091      {
1092         if (!strcmp(tmp_param, "off")) fmt->style = EVAS_TEXT_STYLE_PLAIN;
1093         else if (!strcmp(tmp_param, "none")) fmt->style = EVAS_TEXT_STYLE_PLAIN;
1094         else if (!strcmp(tmp_param, "plain")) fmt->style = EVAS_TEXT_STYLE_PLAIN;
1095         else if (!strcmp(tmp_param, "shadow")) fmt->style = EVAS_TEXT_STYLE_SHADOW;
1096         else if (!strcmp(tmp_param, "outline")) fmt->style = EVAS_TEXT_STYLE_OUTLINE;
1097         else if (!strcmp(tmp_param, "soft_outline")) fmt->style = EVAS_TEXT_STYLE_SOFT_OUTLINE;
1098         else if (!strcmp(tmp_param, "outline_shadow")) fmt->style = EVAS_TEXT_STYLE_OUTLINE_SHADOW;
1099         else if (!strcmp(tmp_param, "outline_soft_shadow")) fmt->style = EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW;
1100         else if (!strcmp(tmp_param, "glow")) fmt->style = EVAS_TEXT_STYLE_GLOW;
1101         else if (!strcmp(tmp_param, "far_shadow")) fmt->style = EVAS_TEXT_STYLE_FAR_SHADOW;
1102         else if (!strcmp(tmp_param, "soft_shadow")) fmt->style = EVAS_TEXT_STYLE_SOFT_SHADOW;
1103         else if (!strcmp(tmp_param, "far_soft_shadow")) fmt->style = EVAS_TEXT_STYLE_FAR_SOFT_SHADOW;
1104         else fmt->style = EVAS_TEXT_STYLE_PLAIN;
1105      }
1106    else if (cmd == tabstopsstr)
1107      {
1108         fmt->tabstops = atoi(tmp_param);
1109         if (fmt->tabstops < 1) fmt->tabstops = 1;
1110      }
1111    else if (cmd == linesizestr)
1112      {
1113         fmt->linesize = atoi(tmp_param);
1114         fmt->linerelsize = 0.0;
1115      }
1116    else if (cmd == linerelsizestr)
1117      {
1118         char *endptr = NULL;
1119         double val = strtod(tmp_param, &endptr);
1120         if (endptr)
1121           {
1122              while (*endptr && _is_white(*endptr))
1123                endptr++;
1124              if (*endptr == '%')
1125                {
1126                   fmt->linerelsize = val / 100.0;
1127                   fmt->linesize = 0;
1128                   if (fmt->linerelsize < 0.0) fmt->linerelsize = 0.0;
1129                }
1130           }
1131      }
1132    else if (cmd == linegapstr)
1133      {
1134         fmt->linegap = atoi(tmp_param);
1135         fmt->linerelgap = 0.0;
1136      }
1137    else if (cmd == linerelgapstr)
1138      {
1139         char *endptr = NULL;
1140         double val = strtod(tmp_param, &endptr);
1141         if (endptr)
1142           {
1143              while (*endptr && _is_white(*endptr))
1144                endptr++;
1145              if (*endptr == '%')
1146                {
1147                   fmt->linerelgap = val / 100.0;
1148                   fmt->linegap = 0;
1149                   if (fmt->linerelgap < 0.0) fmt->linerelgap = 0.0;
1150                }
1151           }
1152      }
1153
1154    if (new_font)
1155      {
1156         void *of;
1157         char *buf = NULL;
1158
1159         of = fmt->font.font;
1160         if ((fmt->font.name) && (fmt->font.fallbacks))
1161           {
1162              buf = malloc(strlen(fmt->font.name) + 1 + strlen(fmt->font.fallbacks) + 1);
1163              strcpy(buf, fmt->font.name);
1164              strcat(buf, ",");
1165              strcat(buf, fmt->font.fallbacks);
1166           }
1167         else if (fmt->font.name)
1168           buf = strdup(fmt->font.name);
1169
1170         fmt->font.font = evas_font_load(obj->layer->evas,
1171                                         buf, fmt->font.source,
1172                                         (int)(((double)fmt->font.size) * obj->cur.scale));
1173         if (buf) free(buf);
1174         if (of) evas_font_free(obj->layer->evas, of);
1175      }
1176 }
1177
1178 static int
1179 _format_is_param(char *item)
1180 {
1181    if (strchr(item, '=')) return 1;
1182    return 0;
1183 }
1184
1185 static void
1186 _format_param_parse(char *item, const char **key, const char **val)
1187 {
1188    char *p;
1189    const char *k, *v;
1190
1191    p = strchr(item, '=');
1192    *p = '\0';
1193    k = eina_stringshare_add(item);
1194    *key = k;
1195    *p = '=';
1196    p++;
1197    v = eina_stringshare_add(p);
1198    *val = v;
1199 }
1200
1201 static char *
1202 _format_parse(char **s)
1203 {
1204    char *p, *item;
1205    char *s1 = NULL, *s2 = NULL;
1206
1207    p = *s;
1208    if (*p == 0) return NULL;
1209    for (;;)
1210      {
1211         if (!s1)
1212           {
1213              if (*p != ' ') s1 = p;
1214              if (*p == 0) break;
1215           }
1216         else if (!s2)
1217           {
1218              if ((p > *s) && (p[-1] != '\\'))
1219                {
1220                   if (*p == ' ') s2 = p;
1221                }
1222              if (*p == 0) s2 = p;
1223           }
1224         p++;
1225         if (s1 && s2)
1226           {
1227              item = s1;
1228
1229              *s = s2;
1230              return item;
1231           }
1232      }
1233    *s = p;
1234    return NULL;
1235 }
1236
1237 static void
1238 _format_fill(Evas_Object *obj, Evas_Object_Textblock_Format *fmt, char *str)
1239 {
1240    char *s;
1241    char *item;
1242
1243    s = str;
1244
1245    /* get rid of anything +s or -s off the start of the string */
1246    while ((*s == ' ') || (*s == '+') || (*s == '-')) s++;
1247
1248    while ((item = _format_parse(&s)))
1249      {
1250         char tmp_delim = *s;
1251         *s = '\0';
1252         if (_format_is_param(item))
1253           {
1254              const char *key = NULL, *val = NULL;
1255
1256              _format_param_parse(item, &key, &val);
1257              _format_command(obj, fmt, key, val);
1258              eina_stringshare_del(key);
1259              eina_stringshare_del(val);
1260           }
1261         else
1262           {
1263              /* immediate - not handled here */
1264           }
1265         *s = tmp_delim;
1266      }
1267 }
1268
1269 static Evas_Object_Textblock_Format *
1270 _format_dup(Evas_Object *obj, Evas_Object_Textblock_Format *fmt)
1271 {
1272    Evas_Object_Textblock_Format *fmt2;
1273    char *buf = NULL;
1274
1275    fmt2 = calloc(1, sizeof(Evas_Object_Textblock_Format));
1276    memcpy(fmt2, fmt, sizeof(Evas_Object_Textblock_Format));
1277    fmt2->ref = 1;
1278    if (fmt->font.name) fmt2->font.name = eina_stringshare_add(fmt->font.name);
1279    if (fmt->font.fallbacks) fmt2->font.fallbacks = eina_stringshare_add(fmt->font.fallbacks);
1280    if (fmt->font.source) fmt2->font.source = eina_stringshare_add(fmt->font.source);
1281
1282    if ((fmt2->font.name) && (fmt2->font.fallbacks))
1283      {
1284         buf = malloc(strlen(fmt2->font.name) + 1 + strlen(fmt2->font.fallbacks) + 1);
1285         strcpy(buf, fmt2->font.name);
1286         strcat(buf, ",");
1287         strcat(buf, fmt2->font.fallbacks);
1288      }
1289    else if (fmt2->font.name)
1290      buf = strdup(fmt2->font.name);
1291    fmt2->font.font = evas_font_load(obj->layer->evas,
1292                                     buf, fmt2->font.source,
1293                                     (int)(((double)fmt2->font.size) * obj->cur.scale));
1294    if (buf) free(buf);
1295    return fmt2;
1296 }
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307 typedef struct _Ctxt Ctxt;
1308
1309 struct _Ctxt
1310 {
1311    Evas_Object *obj;
1312    Evas_Object_Textblock *o;
1313
1314    Evas_Object_Textblock_Line *lines;
1315    Evas_Object_Textblock_Line *ln;
1316
1317    Eina_List *format_stack;
1318
1319    int x, y;
1320    int w, h;
1321    int wmax, hmax;
1322    int maxascent, maxdescent;
1323    int marginl, marginr;
1324    int line_no;
1325    int underline_extend;
1326    int have_underline, have_underline2;
1327    double align;
1328 };
1329
1330 static void
1331 _layout_format_ascent_descent_adjust(Ctxt *c, Evas_Object_Textblock_Format *fmt)
1332 {
1333    int ascent, descent;
1334
1335    if (fmt->font.font)
1336      {
1337 //      ascent = c->ENFN->font_max_ascent_get(c->ENDT, fmt->font.font);
1338 //      descent = c->ENFN->font_max_descent_get(c->ENDT, fmt->font.font);
1339         ascent = c->ENFN->font_ascent_get(c->ENDT, fmt->font.font);
1340         descent = c->ENFN->font_descent_get(c->ENDT, fmt->font.font);
1341         if (fmt->linesize > 0)
1342           {
1343              if ((ascent + descent) < fmt->linesize)
1344                {
1345                   ascent = ((fmt->linesize * ascent) / (ascent + descent));
1346                   descent = fmt->linesize - ascent;
1347                }
1348           }
1349         else if (fmt->linerelsize > 0.0)
1350           {
1351              descent = ((ascent + descent) * fmt->linerelsize) - (ascent * fmt->linerelsize);
1352              ascent = ascent * fmt->linerelsize;
1353           }
1354         c->maxdescent += fmt->linegap;
1355         c->maxdescent += ((ascent + descent) * fmt->linerelgap);
1356         if (c->maxascent < ascent) c->maxascent = ascent;
1357         if (c->maxdescent < descent) c->maxdescent = descent;
1358      }
1359 }
1360
1361 static void
1362 _layout_line_new(Ctxt *c, Evas_Object_Textblock_Format *fmt)
1363 {
1364    c->ln = calloc(1, sizeof(Evas_Object_Textblock_Line));
1365    c->align = fmt->halign;
1366    c->marginl = fmt->margin.l;
1367    c->marginr = fmt->margin.r;
1368    c->lines = (Evas_Object_Textblock_Line *)eina_inlist_append(EINA_INLIST_GET(c->lines), EINA_INLIST_GET(c->ln));
1369    c->x = 0;
1370    c->maxascent = c->maxdescent = 0;
1371    c->ln->line_no = -1;
1372    _layout_format_ascent_descent_adjust(c, fmt);
1373 }
1374
1375 static Evas_Object_Textblock_Format *
1376 _layout_format_push(Ctxt *c, Evas_Object_Textblock_Format *fmt)
1377 {
1378    if (fmt)
1379      {
1380         fmt = _format_dup(c->obj, fmt);
1381         c->format_stack  = eina_list_prepend(c->format_stack, fmt);
1382      }
1383    else
1384      {
1385         fmt = calloc(1, sizeof(Evas_Object_Textblock_Format));
1386         c->format_stack  = eina_list_prepend(c->format_stack, fmt);
1387         fmt->ref = 1;
1388         fmt->halign = 0.0;
1389         fmt->valign = -1.0;
1390         fmt->style = EVAS_TEXT_STYLE_PLAIN;
1391         fmt->tabstops = 32;
1392         fmt->linesize = 0;
1393         fmt->linerelsize = 0.0;
1394         fmt->linegap = 0;
1395         fmt->linerelgap = 0.0;
1396      }
1397    return fmt;
1398 }
1399
1400 static Evas_Object_Textblock_Format *
1401 _layout_format_pop(Ctxt *c, Evas_Object_Textblock_Format *fmt)
1402 {
1403    if ((c->format_stack) && (c->format_stack->next))
1404      {
1405         _format_free(c->obj, fmt);
1406         c->format_stack = eina_list_remove_list(c->format_stack, c->format_stack);
1407         fmt = c->format_stack->data;
1408      }
1409    return fmt;
1410 }
1411
1412 static void
1413 _layout_format_value_handle(Ctxt *c, Evas_Object_Textblock_Format *fmt, char *item)
1414 {
1415    const char *key = NULL, *val = NULL;
1416
1417    _format_param_parse(item, &key, &val);
1418    if ((key) && (val)) _format_command(c->obj, fmt, key, val);
1419    if (key) eina_stringshare_del(key);
1420    if (val) eina_stringshare_del(val);
1421    c->align = fmt->halign;
1422    c->marginl = fmt->margin.l;
1423    c->marginr = fmt->margin.r;
1424 }
1425
1426 #define VSIZE_FULL 0
1427 #define VSIZE_ASCENT 1
1428
1429 #define SIZE 0
1430 #define SIZE_ABS 1
1431 #define SIZE_REL 2
1432
1433 static void
1434 _layout_line_advance(Ctxt *c, Evas_Object_Textblock_Format *fmt)
1435 {
1436    Evas_Object_Textblock_Item *it;
1437    Evas_Object_Textblock_Format_Item *fi;
1438
1439    c->maxascent = c->maxdescent = 0;
1440    if (!c->ln->items)
1441      _layout_format_ascent_descent_adjust(c, fmt);
1442    EINA_INLIST_FOREACH(c->ln->items, it)
1443      {
1444         int endx;
1445
1446         if (it->format->font.font)
1447           it->baseline = c->ENFN->font_max_ascent_get(c->ENDT, it->format->font.font);
1448         _layout_format_ascent_descent_adjust(c, it->format);
1449         endx = it->x + it->w;
1450         if (endx > c->ln->w) c->ln->w = endx;
1451      }
1452    EINA_INLIST_FOREACH(c->ln->format_items, fi)
1453      {
1454         int endx;
1455
1456         if (!fi->formatme) continue;
1457         endx = fi->x + fi->w;
1458         if (endx > c->ln->w) c->ln->w = endx;
1459         switch (fi->size)
1460           {
1461           case SIZE:
1462           case SIZE_ABS:
1463              switch (fi->vsize)
1464                {
1465                case VSIZE_FULL:
1466                   if (fi->h > (c->maxdescent + c->maxascent))
1467                     {
1468                        c->maxascent += fi->h - (c->maxdescent + c->maxascent);
1469                        fi->y = -c->maxascent;
1470                     }
1471                   else
1472                     fi->y = -(fi->h - c->maxdescent);
1473                   break;
1474                case VSIZE_ASCENT:
1475                   if (fi->h > c->maxascent)
1476                     {
1477                        c->maxascent = fi->h;
1478                        fi->y = -fi->h;
1479                     }
1480                   else
1481                     fi->y = -fi->h;
1482                   break;
1483                default:
1484                   break;
1485                }
1486              break;
1487           case SIZE_REL:
1488              switch (fi->vsize)
1489                {
1490                case VSIZE_FULL:
1491                case VSIZE_ASCENT:
1492                   fi->y = -fi->ascent;
1493                   break;
1494                default:
1495                   break;
1496                }
1497              break;
1498           default:
1499              break;
1500           }
1501      }
1502    c->ln->y = c->y + c->o->style_pad.t;
1503    c->ln->h = c->maxascent + c->maxdescent;
1504    c->ln->baseline = c->maxascent;
1505    if (c->have_underline2)
1506      {
1507         if (c->maxdescent < 4) c->underline_extend = 4 - c->maxdescent;
1508      }
1509    else if (c->have_underline)
1510      {
1511         if (c->maxdescent < 2) c->underline_extend = 2 - c->maxdescent;
1512      }
1513    c->ln->line_no = c->line_no;
1514    c->line_no++;
1515    c->y += c->maxascent + c->maxdescent;
1516    if (c->w >= 0)
1517      {
1518         c->ln->x = c->marginl + c->o->style_pad.l +
1519           ((c->w - c->ln->w -
1520             c->o->style_pad.l - c->o->style_pad.r -
1521             c->marginl - c->marginr) * c->align);
1522         if ((c->ln->x + c->ln->w + c->marginr - c->o->style_pad.l) > c->wmax)
1523           c->wmax = c->ln->x + c->ln->w + c->marginl + c->marginr - c->o->style_pad.l;
1524      }
1525    else
1526      {
1527         c->ln->x = c->marginl + c->o->style_pad.l;
1528         if ((c->ln->x + c->ln->w + c->marginr - c->o->style_pad.l) > c->wmax)
1529           c->wmax = c->ln->x + c->ln->w + c->marginl + c->marginr - c->o->style_pad.l;
1530      }
1531    _layout_line_new(c, fmt);
1532 }
1533
1534 static Evas_Object_Textblock_Item *
1535 _layout_item_new(Ctxt *c __UNUSED__, Evas_Object_Textblock_Format *fmt, char *str)
1536 {
1537    Evas_Object_Textblock_Item *it;
1538
1539    it = calloc(1, sizeof(Evas_Object_Textblock_Item));
1540    it->format = fmt;
1541    it->format->ref++;
1542    it->text = strdup(str);
1543    return it;
1544 }
1545
1546 static int
1547 _layout_text_cutoff_get(Ctxt *c, Evas_Object_Textblock_Format *fmt, Evas_Object_Textblock_Item *it)
1548 {
1549
1550    if (fmt->font.font)
1551      return c->ENFN->font_last_up_to_pos(c->ENDT, fmt->font.font, it->text,
1552                                              c->w -
1553                                              c->o->style_pad.l -
1554                                              c->o->style_pad.r -
1555                                              c->marginl -
1556                                              c->marginr -
1557                                              c->x,
1558                                              0);
1559    return -1;
1560 }
1561
1562 static void
1563 _layout_item_text_cutoff(Ctxt *c __UNUSED__, Evas_Object_Textblock_Item *it, int cut)
1564 {
1565    char *ts;
1566
1567    ts = it->text;
1568    ts[cut] = 0;
1569    it->text = strdup(ts);
1570    free(ts);
1571 }
1572
1573 static int
1574 _layout_word_start(char *str, int start)
1575 {
1576    int p, tp, chr = 0;
1577
1578    p = start;
1579    chr = evas_common_font_utf8_get_next((unsigned char *)(str), &p);
1580    if (_is_white(chr))
1581      {
1582         tp = p;
1583         while (_is_white(chr) && (p >= 0))
1584           {
1585              tp = p;
1586              chr = evas_common_font_utf8_get_next((unsigned char *)(str), &p);
1587           }
1588         return tp;
1589      }
1590    p = start;
1591    tp = p;
1592    while (p > 0)
1593      {
1594         chr = evas_common_font_utf8_get_prev((unsigned char *)(str), &p);
1595         if (_is_white(chr)) break;
1596         tp = p;
1597      }
1598    if (p < 0) p = 0;
1599    if ((p >= 0) && (_is_white(chr)))
1600      evas_common_font_utf8_get_next((unsigned char *)(str), &p);
1601    return p;
1602 }
1603
1604 static int
1605 _layout_ends_with_space(char *str)
1606 {
1607    int p, chr;
1608
1609    p = evas_common_font_utf8_get_last((unsigned char *)(str), strlen(str));
1610    if (p < 0) return 0;
1611    chr = evas_common_font_utf8_get_next((unsigned char *)(str), &p);
1612    return _is_white(chr);
1613 }
1614
1615 static int
1616 _layout_strip_trailing_whitespace(Ctxt *c, Evas_Object_Textblock_Format *fmt __UNUSED__, Evas_Object_Textblock_Item *it)
1617 {
1618    int p, tp, chr, adv, tw, th;
1619
1620    p = evas_common_font_utf8_get_last((unsigned char *)(it->text), strlen(it->text));
1621    tp = p;
1622    if (p >= 0)
1623      {
1624         chr = evas_common_font_utf8_get_prev((unsigned char *)(it->text), &p);
1625         if (_is_white(chr))
1626           {
1627              _layout_item_text_cutoff(c, it, tp);
1628              adv = 0;
1629              if (it->format->font.font)
1630                adv = c->ENFN->font_h_advance_get(c->ENDT, it->format->font.font, it->text);
1631              tw = th = 0;
1632              if (it->format->font.font)
1633                c->ENFN->font_string_size_get(c->ENDT, it->format->font.font, it->text, &tw, &th);
1634              it->w = tw;
1635              it->h = th;
1636              c->x = it->x + adv;
1637              return 1;
1638           }
1639      }
1640    return 0;
1641 }
1642
1643 static int
1644 _layout_item_abort(Ctxt *c, Evas_Object_Textblock_Format *fmt, Evas_Object_Textblock_Item *it)
1645 {
1646    if (it->text) free(it->text);
1647    _format_free(c->obj, it->format);
1648    free(it);
1649    if (c->ln->items)
1650      {
1651         it = (Evas_Object_Textblock_Item *)(EINA_INLIST_GET(c->ln->items))->last;
1652         return _layout_strip_trailing_whitespace(c, fmt, it);
1653      }
1654    return 0;
1655 }
1656
1657 #if 0
1658 static char *
1659 _layout_next_char_jump(Ctxt *c, Evas_Object_Textblock_Item *it, char *str)
1660 {
1661    int index;
1662
1663    index = 0;
1664    evas_common_font_utf8_get_next((unsigned char *)str, &index);
1665    if (index >= 0)
1666      {
1667         str = str + index;
1668         _layout_item_text_cutoff(c, it, index);
1669      }
1670    else
1671      str = NULL;
1672    return str;
1673 }
1674 #endif
1675
1676 static int
1677 _layout_last_item_ends_in_whitespace(Ctxt *c)
1678 {
1679    Evas_Object_Textblock_Item *it;
1680
1681    if (!c->ln->items) return 1;
1682    it = (Evas_Object_Textblock_Item *)(EINA_INLIST_GET(c->ln->items))->last;
1683    return _layout_ends_with_space(it->text);
1684 }
1685
1686 static int
1687 _layout_word_end(char *str, int p)
1688 {
1689    int ch, tp;
1690
1691    tp = p;
1692    ch = evas_common_font_utf8_get_next((unsigned char *)str, &tp);
1693    while ((!_is_white(ch)) && (tp >= 0) && (ch != 0))
1694      {
1695         p = tp;
1696         ch = evas_common_font_utf8_get_next((unsigned char *)str, &tp);
1697      }
1698    if (ch == 0) return -1;
1699    return p;
1700 }
1701
1702 static int
1703 _layout_word_next(char *str, int p)
1704 {
1705    int ch, tp;
1706
1707    tp = p;
1708    ch = evas_common_font_utf8_get_next((unsigned char *)str, &tp);
1709    while ((!_is_white(ch)) && (tp >= 0) && (ch != 0))
1710      {
1711         p = tp;
1712         ch = evas_common_font_utf8_get_next((unsigned char *)str, &tp);
1713      }
1714    if (ch == 0) return -1;
1715    while ((_is_white(ch)) && (tp >= 0) && (ch != 0))
1716      {
1717         p = tp;
1718         ch = evas_common_font_utf8_get_next((unsigned char *)str, &tp);
1719      }
1720    if (ch == 0) return -1;
1721    return p;
1722 }
1723
1724 static void
1725 _layout_walk_back_to_item_word_redo(Ctxt *c, Evas_Object_Textblock_Item *it)
1726 {
1727    Evas_Object_Textblock_Item *pit, *new_it = NULL;
1728    Eina_List *remove_items = NULL, *l;
1729    Eina_Inlist *data;
1730    int index, tw, th, inset, adv;
1731
1732    /* it is not appended yet */
1733    EINA_INLIST_REVERSE_FOREACH((EINA_INLIST_GET(c->ln->items)), pit)
1734      {
1735         if (_layout_ends_with_space(pit->text))
1736           {
1737              break;
1738           }
1739         index = evas_common_font_utf8_get_last((unsigned char *)(pit->text), strlen(pit->text));
1740         index = _layout_word_start(pit->text, index);
1741         if (index == 0)
1742           remove_items = eina_list_prepend(remove_items, pit);
1743         else
1744           {
1745              new_it = _layout_item_new(c, pit->format, pit->text + index);
1746              new_it->source_node = pit->source_node;
1747              new_it->source_pos = pit->source_pos + index;
1748              _layout_item_text_cutoff(c, pit, index);
1749              _layout_strip_trailing_whitespace(c, pit->format, pit);
1750              break;
1751           }
1752      }
1753    EINA_LIST_FOREACH(remove_items, l, data)
1754      c->ln->items = (Evas_Object_Textblock_Item *)eina_inlist_remove(EINA_INLIST_GET(c->ln->items), data);
1755    /* new line now */
1756    if (remove_items)
1757      {
1758         pit = remove_items->data;
1759         _layout_line_advance(c, pit->format);
1760      }
1761    else
1762      {
1763         _layout_line_advance(c, it->format);
1764      }
1765    if (new_it)
1766      {
1767         /* append new_it */
1768         tw = th = 0;
1769         if (new_it->format->font.font)
1770           c->ENFN->font_string_size_get(c->ENDT, new_it->format->font.font, new_it->text, &tw, &th);
1771         new_it->w = tw;
1772         new_it->h = th;
1773         inset = 0;
1774         if (new_it->format->font.font)
1775           inset = c->ENFN->font_inset_get(c->ENDT, new_it->format->font.font, new_it->text);
1776         new_it->inset = inset;
1777         new_it->x = c->x;
1778         adv = 0;
1779         if (new_it->format->font.font)
1780           adv = c->ENFN->font_h_advance_get(c->ENDT, new_it->format->font.font, new_it->text);
1781         c->x += adv;
1782         c->ln->items = (Evas_Object_Textblock_Item *)eina_inlist_append(EINA_INLIST_GET(c->ln->items), EINA_INLIST_GET(new_it));
1783      }
1784    while (remove_items)
1785      {
1786         pit = remove_items->data;
1787         remove_items = eina_list_remove_list(remove_items, remove_items);
1788         /* append pit */
1789         pit->x = c->x;
1790         adv = c->ENFN->font_h_advance_get(c->ENDT, pit->format->font.font, pit->text);
1791         c->x += adv;
1792         c->ln->items = (Evas_Object_Textblock_Item *)eina_inlist_append(EINA_INLIST_GET(c->ln->items), EINA_INLIST_GET(pit));
1793      }
1794    if (it)
1795      {
1796         /* append it */
1797         it->x = c->x;
1798         adv = 0;
1799         if (it->format->font.font)
1800           adv = c->ENFN->font_h_advance_get(c->ENDT, it->format->font.font, it->text);
1801         c->x += adv;
1802         c->ln->items = (Evas_Object_Textblock_Item *)eina_inlist_append(EINA_INLIST_GET(c->ln->items), EINA_INLIST_GET(it));
1803      }
1804 }
1805
1806 static void
1807 _layout_text_append(Ctxt *c, Evas_Object_Textblock_Format *fmt, Evas_Object_Textblock_Node *n, const char *repch)
1808 {
1809    int adv, inset, tw, th, new_line, empty_item;
1810    int wrap, twrap, ch, index, white_stripped;
1811    char *str;
1812    const char *tbase;
1813    Evas_Object_Textblock_Item *it;
1814
1815    if (n)
1816      {
1817         if ((repch) && (eina_strbuf_length_get(n->text)))
1818           {
1819              int i, len, chlen;
1820              char *ptr;
1821              
1822              len = evas_common_font_utf8_get_len((unsigned char *) eina_strbuf_string_get(n->text));
1823              chlen = strlen(repch);
1824              str = alloca((len * chlen) + 1);
1825              tbase = str;
1826              for (i = 0, ptr = str; i < len; ptr += chlen, i++)
1827                memcpy(ptr, repch, chlen);
1828              *ptr = 0;
1829           }
1830         else
1831           {
1832              str = (char *)eina_strbuf_string_get(n->text);
1833              tbase = str;
1834           }
1835      }
1836    else
1837      {
1838         str = "";
1839         tbase = str;
1840      }
1841 //   printf("add: wrap: %i|%i, width: %i '%s'\n", fmt->wrap_word, fmt->wrap_char, c->w, str);
1842    new_line = 0;
1843    empty_item = 0;
1844    while (str)
1845      {
1846         /* if this is the first line item and it starts with spaces - remove them */
1847         wrap = 0;
1848         white_stripped = 0;
1849 /*
1850         if (!c->ln->items)
1851           {
1852              twrap = wrap;
1853              ch = evas_common_font_utf8_get_next((unsigned char *)str, &wrap);
1854              while (_is_white(ch))
1855                {
1856                   twrap = wrap;
1857                   ch = evas_common_font_utf8_get_next((unsigned char *)str, &wrap);
1858                }
1859              str = str + twrap;
1860           }
1861  */
1862         it = _layout_item_new(c, fmt, str);
1863         it->source_node = n;
1864         it->source_pos = str - tbase;
1865         tw = th = 0;
1866         if (fmt->font.font)
1867           c->ENFN->font_string_size_get(c->ENDT, fmt->font.font, it->text, &tw, &th);
1868         if ((c->w >= 0) &&
1869             ((fmt->wrap_word) || (fmt->wrap_char)) &&
1870             ((c->x + tw) >
1871              (c->w - c->o->style_pad.l - c->o->style_pad.r -
1872               c->marginl - c->marginr)))
1873           {
1874              wrap = _layout_text_cutoff_get(c, fmt, it);
1875              if (wrap == 0)
1876                evas_common_font_utf8_get_next((unsigned char *)str, &wrap);
1877              if (wrap > 0)
1878                {
1879                   if (fmt->wrap_word)
1880                     {
1881                        index = wrap;
1882                        ch = evas_common_font_utf8_get_next((unsigned char *)str, &index);
1883                        if (!_is_white(ch))
1884                          wrap = _layout_word_start(str, wrap);
1885                        if (wrap > 0)
1886                          {
1887                             twrap = wrap;
1888                             ch = evas_common_font_utf8_get_prev((unsigned char *)str, &twrap);
1889                             /* the text intersects the wrap point on a whitespace char */
1890                             if (_is_white(ch))
1891                               {
1892                                  _layout_item_text_cutoff(c, it, wrap);
1893                                  twrap = wrap;
1894                                  /*we don't want to move next, that's why it's
1895                                   * commented out.
1896                                   * ch = evas_common_font_utf8_get_next((unsigned char *)str, &twrap);
1897                                   */
1898                                  str = str + twrap;
1899                               }
1900                             /* intersects a word */
1901                             else
1902                               {
1903                                  /* walk back to start of word */
1904                                  twrap = _layout_word_start(str, wrap);
1905                                  if (twrap != 0)
1906                                    {
1907                                       wrap = twrap;
1908                                       ch = evas_common_font_utf8_get_prev((unsigned char *)str, &twrap);
1909                                       _layout_item_text_cutoff(c, it, twrap);
1910                                       str = str + wrap;
1911                                    }
1912                                  else
1913                                    {
1914                                       empty_item = 1;
1915                                       if (it->text) free(it->text);
1916                                       _format_free(c->obj, it->format);
1917                                       free(it);
1918                                       if (c->ln->items)
1919                                         {
1920                                            it = (Evas_Object_Textblock_Item *)(EINA_INLIST_GET(c->ln->items))->last;
1921                                            _layout_strip_trailing_whitespace(c, fmt, it);
1922                                            twrap = _layout_word_end(str, wrap);
1923                                            if (twrap >= 0)
1924                                              {
1925                                                 ch = evas_common_font_utf8_get_next((unsigned char *)str, &twrap);
1926                                                 str = str + twrap;
1927                                              }
1928                                            else
1929                                              str = NULL;
1930                                         }
1931                                    }
1932                               }
1933                          }
1934                        else
1935                          {
1936                             /* wrap now is the index of the word START */
1937                             index = wrap;
1938                             ch = evas_common_font_utf8_get_next((unsigned char *)str, &index);
1939                             if (!_is_white(ch) &&
1940                                 (!_layout_last_item_ends_in_whitespace(c)))
1941                               {
1942                                  _layout_walk_back_to_item_word_redo(c, it);
1943                                  return;
1944                               }
1945                             if (c->ln->items != NULL)
1946                               {
1947                                  white_stripped = _layout_item_abort(c, fmt, it);
1948                                  empty_item = 1;
1949                               }
1950                             else
1951                               {
1952                                  if (wrap <= 0)
1953                                    {
1954                                       wrap = 0;
1955                                       twrap = _layout_word_end(it->text, wrap);
1956                                       wrap = twrap;
1957                                       if (twrap >= 0)
1958                                         {
1959                                            ch = evas_common_font_utf8_get_next((unsigned char *)str, &wrap);
1960                                            _layout_item_text_cutoff(c, it, twrap);
1961                                         }
1962                                       if (wrap > 0)
1963                                         str = str + wrap;
1964                                       else
1965                                         str = NULL;
1966                                    }
1967                                  else
1968                                    str = NULL;
1969                               }
1970                          }
1971                     }
1972                   else if (fmt->wrap_char)
1973                     {
1974                        _layout_item_text_cutoff(c, it, wrap);
1975                        str = str + wrap;
1976                     }
1977                   new_line = 1;
1978                }
1979              else
1980                {
1981                   /* wrap now is the index of the word START */
1982                   if (wrap <= 0)
1983                     {
1984                        if (wrap < 0) wrap = 0;
1985                        index = wrap;
1986                        ch = evas_common_font_utf8_get_next((unsigned char *)str, &index);
1987                        if (!_is_white(ch) &&
1988                            (!_layout_last_item_ends_in_whitespace(c)))
1989                          {
1990                             _layout_walk_back_to_item_word_redo(c, it);
1991                             return;
1992                          }
1993                     }
1994                   if (c->ln->items != NULL)
1995                     {
1996                        white_stripped = _layout_item_abort(c, fmt, it);
1997                        empty_item = 1;
1998                        new_line = 1;
1999                     }
2000                   else
2001                     {
2002                        if (wrap <= 0)
2003                          {
2004                             wrap = 0;
2005                             twrap = _layout_word_end(it->text, wrap);
2006                             wrap = _layout_word_next(it->text, wrap);
2007                             if (twrap >= 0)
2008                               _layout_item_text_cutoff(c, it, twrap);
2009                             if (wrap >= 0)
2010                               str = str + wrap;
2011                             else
2012                               str = NULL;
2013                          }
2014                        else
2015                          str = NULL;
2016                        new_line = 1;
2017                     }
2018                }
2019              if (!empty_item)
2020                {
2021                   tw = th = 0;
2022                   if (fmt->font.font)
2023                     c->ENFN->font_string_size_get(c->ENDT, fmt->font.font, it->text, &tw, &th);
2024                }
2025           }
2026         else
2027           str = NULL;
2028         if (empty_item) empty_item = 0;
2029         else
2030           {
2031              it->w = tw;
2032              it->h = th;
2033              inset = 0;
2034              if (fmt->font.font)
2035                inset = c->ENFN->font_inset_get(c->ENDT, fmt->font.font, it->text);
2036              it->inset = inset;
2037              it->x = c->x;
2038              adv = 0;
2039              if (fmt->font.font)
2040                adv = c->ENFN->font_h_advance_get(c->ENDT, fmt->font.font, it->text);
2041              c->x += adv;
2042              c->ln->items = (Evas_Object_Textblock_Item *)eina_inlist_append(EINA_INLIST_GET(c->ln->items), EINA_INLIST_GET(it));
2043           }
2044         if (new_line)
2045           {
2046              if (str)
2047                {
2048                   if (!white_stripped)
2049                     {
2050                        index = 0;
2051                        ch = evas_common_font_utf8_get_next((unsigned char *)str, &index);
2052                        if (_is_white(ch)) str += index;
2053                     }
2054                }
2055              new_line = 0;
2056              _layout_line_advance(c, fmt);
2057           }
2058      }
2059 }
2060
2061 static Evas_Object_Textblock_Format_Item *
2062 _layout_format_item_add(Ctxt *c, Evas_Object_Textblock_Node *n, char *item)
2063 {
2064    Evas_Object_Textblock_Format_Item *fi;
2065
2066    fi = calloc(1, sizeof(Evas_Object_Textblock_Format_Item));
2067    fi->item = eina_stringshare_add(item);
2068    fi->source_node = n;
2069    c->ln->format_items = (Evas_Object_Textblock_Format_Item *)eina_inlist_append(EINA_INLIST_GET(c->ln->format_items),
2070                                                                                  EINA_INLIST_GET(fi));
2071    return fi;
2072 }
2073
2074 static void
2075 _layout(const Evas_Object *obj, int calc_only, int w, int h, int *w_ret, int *h_ret)
2076 {
2077    Evas_Object_Textblock *o;
2078    Ctxt ctxt, *c;
2079    Evas_Object_Textblock_Line *ln;
2080    Evas_Object_Textblock_Node *n;
2081    Eina_List *removes = NULL;
2082    Evas_Object_Textblock_Format *fmt = NULL;
2083    int style_pad_l = 0, style_pad_r = 0, style_pad_t = 0, style_pad_b = 0;
2084
2085    /* setup context */
2086    o = (Evas_Object_Textblock *)(obj->object_data);
2087    c = &ctxt;
2088    c->obj = (Evas_Object *)obj;
2089    c->o = o;
2090    c->lines = c->ln = NULL;
2091    c->format_stack = NULL;
2092    c->x = c->y = 0;
2093    c->w = w;
2094    c->h = h;
2095    c->wmax = c->hmax = 0;
2096    c->maxascent = c->maxdescent = 0;
2097    c->marginl = c->marginr = 0;
2098    c->have_underline = 0;
2099    c->have_underline2 = 0;
2100    c->underline_extend = 0;
2101    c->line_no = 0;
2102    c->align = 0.0;
2103
2104    _format_command_init();
2105    /* setup default base style */
2106    if ((c->o->style) && (c->o->style->default_tag))
2107      {
2108         fmt = _layout_format_push(c, NULL);
2109         _format_fill(c->obj, fmt, c->o->style->default_tag);
2110      }
2111    if (!fmt)
2112      {
2113         _format_command_shutdown();
2114         if (w_ret) *w_ret = 0;
2115         if (h_ret) *h_ret = 0;
2116         return;
2117      }
2118    /* run through all text and format nodes generating lines */
2119    if (!c->o->nodes)
2120      {
2121         if (!c->ln)
2122           {
2123              _layout_line_new(c, fmt);
2124              _layout_text_append(c, fmt, NULL, NULL);
2125              _layout_line_advance(c, fmt);
2126 //             printf("bl:%i | %ix%i\n", c->ln->baseline, c->ln->w, c->ln->h);
2127              /*
2128              tw = th = 0;
2129              if (fmt->font.font)
2130                {
2131                   c->ENFN->font_string_size_get(c->ENDT, fmt->font.font, "", &tw, &th);
2132                   c->ln->x = 0;
2133                   c->ln->y = 0;
2134                   c->ln->w = tw;
2135                   c->ln->h = th;
2136                   c->wmax = tw;
2137                   c->hmax = th;
2138                   _layout_format_ascent_descent_adjust(c, fmt);
2139                }
2140               */
2141           }
2142      }
2143    EINA_INLIST_FOREACH(c->o->nodes, n)
2144      {
2145         if (!c->ln) _layout_line_new(c, fmt);
2146         if ((n->type == NODE_FORMAT) && eina_strbuf_length_get(n->text))
2147           {
2148              char *s;
2149              char *item;
2150              int handled = 0;
2151
2152              s = (char *)eina_strbuf_string_get(n->text);
2153              if (!strncmp(s, "+ item ", 7))
2154                {
2155                   // one of:
2156                   //   item size=20x10 href=name
2157                   //   item relsize=20x10 href=name
2158                   //   item abssize=20x10 href=name
2159                   // 
2160                   // optional arguments:
2161                   //   vsize=full
2162                   //   vsize=ascent
2163                   // 
2164                   // size == item size (modifies line size) - can be multiplied by
2165                   //   scale factor
2166                   // relsize == relative size (height is current font height, width
2167                   //   modified accordingly keeping aspect)
2168                   // abssize == absolute size (modifies line size) - never mulitplied by
2169                   //   scale factor
2170                   // href == name of item - to be found and matched later and used for
2171                   //   positioning
2172                   Evas_Object_Textblock_Format_Item *fi;
2173                   int x2, w = 1, h = 1;
2174                   int vsize = 0, size = 0;
2175                   char *p;
2176                   
2177                   // don't care
2178                   //href = strstr(s, " href=");
2179                   p = strstr(s, " vsize=");
2180                   if (p)
2181                     {
2182                        p += 7;
2183                        if (!strncmp(p, "full", 4)) vsize = VSIZE_FULL;
2184                        else if (!strncmp(p, "ascent", 6)) vsize = VSIZE_ASCENT;
2185                     }
2186                   p = strstr(s, " size=");
2187                   if (p)
2188                     {
2189                        p += 6;
2190                        if (sscanf(p, "%ix%i", &w, &h) == 2)
2191                          {
2192                             w = w * obj->cur.scale;
2193                             h = h * obj->cur.scale;
2194                             size = SIZE;
2195                          }
2196                     }
2197                   else
2198                     {
2199                        p = strstr(s, " absize=");
2200                        if (p)
2201                          {
2202                             p += 8;
2203                             if (sscanf(p, "%ix%i", &w, &h) == 2)
2204                               {
2205                                  size = SIZE_ABS;
2206                               }
2207                          }
2208                        else
2209                          {
2210                             p = strstr(s, " relsize=");
2211                             if (p)
2212                               {
2213                                  p += 9;
2214                                  if (sscanf(p, "%ix%i", &w, &h) == 2)
2215                                    {
2216                                       int sz = 1;
2217                                       size = SIZE_REL;
2218                                       if (vsize == VSIZE_FULL)
2219                                         {
2220                                            sz = c->maxdescent + c->maxascent;
2221                                         }
2222                                       else if (vsize == VSIZE_ASCENT)
2223                                         {
2224                                            sz = c->maxascent;
2225                                         }
2226                                       w = (w * sz) / h;
2227                                       h = sz;
2228                                    }
2229                               }
2230                          }
2231                     }
2232
2233                   x2 = c->x + w;
2234                   
2235                   if (x2 >
2236                       (c->w - c->o->style_pad.l -
2237                        c->o->style_pad.r -
2238                        c->marginl - c->marginr))
2239                     {
2240                        _layout_line_advance(c, fmt);
2241                        x2 = w;
2242                     }
2243                   fi = _layout_format_item_add(c, n, item);
2244                   fi->x = c->x;
2245                   fi->vsize = vsize;
2246                   fi->size = size;
2247                   fi->formatme = 1;
2248                   fi->w = w;
2249                   fi->h = h;
2250                   fi->ascent = c->maxascent;
2251                   fi->descent = c->maxdescent;
2252                   c->x = x2;
2253                   handled = 1;
2254                }
2255              if (!handled)
2256                {
2257                   if (s[0] == '+')
2258                     {
2259                        fmt = _layout_format_push(c, fmt);
2260                        s++;
2261                     }
2262                   else if (s[0] == '-')
2263                     {
2264                        fmt = _layout_format_pop(c, fmt);
2265                        s++;
2266                     }
2267                   while ((item = _format_parse(&s)))
2268                     {
2269                        char tmp_delim = *s;
2270                        *s = '\0';
2271                        if (_format_is_param(item))
2272                          {
2273                             _layout_format_value_handle(c, fmt, item);
2274                          }
2275                        else
2276                          {
2277                             if ((!strcmp(item, "\n")) || (!strcmp(item, "\\n")))
2278                               {
2279                                  Evas_Object_Textblock_Format_Item *fi;
2280                                  
2281                                  fi = _layout_format_item_add(c, n, item);
2282                                  fi->x = c->x;
2283                                  fi->w = 0;
2284                                  _layout_line_advance(c, fmt);
2285                               }
2286                             else if ((!strcmp(item, "\t")) || (!strcmp(item, "\\t")))
2287                               {
2288                                  Evas_Object_Textblock_Format_Item *fi;
2289                                  int x2;
2290                                  
2291                                  x2 = (fmt->tabstops * ((c->x + fmt->tabstops) / fmt->tabstops));
2292                                  if (x2 >
2293                                      (c->w - c->o->style_pad.l -
2294                                       c->o->style_pad.r -
2295                                       c->marginl - c->marginr))
2296                                    {
2297                                       _layout_line_advance(c, fmt);
2298                                       x2 = (fmt->tabstops * ((c->x + fmt->tabstops) / fmt->tabstops));
2299                                    }
2300                                  if (c->ln->items)
2301                                    {
2302                                       Evas_Object_Textblock_Item *it;
2303                                       
2304                                       it = (Evas_Object_Textblock_Item *)(EINA_INLIST_GET(c->ln->items))->last;
2305                                       _layout_strip_trailing_whitespace(c, fmt, it);
2306                                    }
2307                                  fi = _layout_format_item_add(c, n, item);
2308                                  fi->x = c->x;
2309                                  fi->w = x2 - c->x;
2310                                  c->x = x2;
2311                               }
2312                          }
2313                        *s = tmp_delim;
2314                     }
2315                }
2316              
2317              evas_text_style_pad_get(fmt->style, &style_pad_l, &style_pad_r, &style_pad_t, &style_pad_b);
2318
2319              if (fmt->underline2)
2320                c->have_underline2 = 1;
2321              else if (fmt->underline)
2322                c->have_underline = 1;
2323           }
2324         else if ((n->type == NODE_TEXT) && eina_strbuf_length_get(n->text))
2325           {
2326              _layout_text_append(c, fmt, n, o->repch);
2327              if ((c->have_underline2) || (c->have_underline))
2328                {
2329                   if (style_pad_b < c->underline_extend)
2330                     style_pad_b = c->underline_extend;
2331                   c->have_underline = 0;
2332                   c->have_underline2 = 0;
2333                   c->underline_extend = 0;
2334                }
2335           }
2336      }
2337    if ((c->ln) && (c->ln->items) && (fmt))
2338      _layout_line_advance(c, fmt);
2339    while (c->format_stack)
2340      {
2341         fmt = c->format_stack->data;
2342         c->format_stack = eina_list_remove_list(c->format_stack, c->format_stack);
2343         _format_free(c->obj, fmt);
2344      }
2345    EINA_INLIST_FOREACH(c->lines, ln)
2346      {
2347         if (ln->line_no == -1)
2348           {
2349              removes = eina_list_append(removes, ln);
2350           }
2351         else
2352           {
2353              if ((ln->y + ln->h) > c->hmax) c->hmax = ln->y + ln->h;
2354           }
2355      }
2356    while (removes)
2357      {
2358         ln = removes->data;
2359         c->lines = (Evas_Object_Textblock_Line *)eina_inlist_remove(EINA_INLIST_GET(c->lines), EINA_INLIST_GET(ln));
2360         removes = eina_list_remove_list(removes, removes);
2361         _line_free(obj, ln);
2362      }
2363
2364    if (w_ret) *w_ret = c->wmax;
2365    if (h_ret) *h_ret = c->hmax;
2366    if ((o->style_pad.l != style_pad_l) || (o->style_pad.r != style_pad_r) ||
2367        (o->style_pad.t != style_pad_t) || (o->style_pad.b != style_pad_b))
2368      {
2369         Evas_Object_Textblock_Line *lines;
2370
2371         lines = c->lines;
2372         c->lines = NULL;
2373         o->style_pad.l = style_pad_l;
2374         o->style_pad.r = style_pad_r;
2375         o->style_pad.t = style_pad_t;
2376         o->style_pad.b = style_pad_b;
2377         _layout(obj, calc_only, w, h, w_ret, h_ret);
2378         _lines_clear(obj, lines);
2379         _format_command_shutdown();
2380         return;
2381      }
2382    if (!calc_only)
2383      {
2384         o->lines = c->lines;
2385         _format_command_shutdown();
2386         return;
2387      }
2388    if (c->lines) _lines_clear(obj, c->lines);
2389    _format_command_shutdown();
2390 }
2391
2392 static void
2393 _relayout(const Evas_Object *obj)
2394 {
2395    Evas_Object_Textblock *o;
2396    Evas_Object_Textblock_Line *lines;
2397
2398    o = (Evas_Object_Textblock *)(obj->object_data);
2399    lines = o->lines;
2400    o->lines = NULL;
2401    o->formatted.valid = 0;
2402    o->native.valid = 0;
2403    _layout(obj,
2404            0,
2405            obj->cur.geometry.w, obj->cur.geometry.h,
2406            &o->formatted.w, &o->formatted.h);
2407    o->formatted.valid = 1;
2408    if (lines) _lines_clear(obj, lines);
2409    o->last_w = obj->cur.geometry.w;
2410    o->changed = 0;
2411    o->redraw = 1;
2412 }
2413
2414 static void
2415 _find_layout_item_line_match(Evas_Object *obj, Evas_Object_Textblock_Node *n, int pos, int eol, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Item **itr)
2416 {
2417    Evas_Object_Textblock_Line *ln;
2418 /*    Evas_Object_Textblock_Node *nn; */
2419    Evas_Object_Textblock *o;
2420
2421    o = (Evas_Object_Textblock *)(obj->object_data);
2422 /*    EINA_INLIST_FOREACH(o->nodes, nn) */
2423 /*      ; */
2424    if ((eol) && (n->type == NODE_TEXT))
2425      {
2426         int pos2 = pos;
2427
2428         evas_common_font_utf8_get_prev((unsigned char *) eina_strbuf_string_get(n->text), &pos2);
2429         if (pos2 < pos) pos = pos2;
2430      }
2431    EINA_INLIST_FOREACH(o->lines, ln)
2432      {
2433         Evas_Object_Textblock_Format_Item *fit;
2434         Evas_Object_Textblock_Item *it;
2435         Evas_Object_Textblock_Line *lnn;
2436
2437         lnn = (Evas_Object_Textblock_Line *)(((Eina_Inlist *)ln)->next);
2438         EINA_INLIST_FOREACH(ln->items, it)
2439           {
2440              if (it->source_node == n)
2441                {
2442                   Evas_Object_Textblock_Item *itn;
2443                   int p;
2444
2445                   itn = (Evas_Object_Textblock_Item *)(((Eina_Inlist *)it)->next);
2446                   p = (int)(it->source_pos + strlen(it->text));
2447                   if ((p >= pos) ||
2448                       ((p == pos) && (!lnn) &&
2449                        ((!itn)  |
2450                         ((itn) && (itn->source_node != n)))))
2451                     {
2452                        *lnr = ln;
2453                        *itr = it;
2454                        return;
2455                     }
2456                }
2457           }
2458         EINA_INLIST_FOREACH(ln->format_items, fit)
2459           {
2460              if (fit->source_node == n)
2461                {
2462                   *lnr = ln;
2463                   /* FIXME: Is that really what we want ? */
2464                   *itr = (Evas_Object_Textblock_Item *)fit;
2465                   return;
2466                }
2467           }
2468      }
2469 }
2470
2471 static void
2472 _find_layout_format_item_line_match(Evas_Object *obj, Evas_Object_Textblock_Node *n, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Format_Item **fir)
2473 {
2474    Evas_Object_Textblock_Line *ln;
2475    Evas_Object_Textblock *o;
2476
2477    o = (Evas_Object_Textblock *)(obj->object_data);
2478    EINA_INLIST_FOREACH(o->lines, ln)
2479      {
2480         Evas_Object_Textblock_Format_Item *fi;
2481
2482         EINA_INLIST_FOREACH(ln->format_items, fi)
2483           {
2484              if (fi->source_node == n)
2485                {
2486                   *lnr = ln;
2487                   *fir = fi;
2488                   return;
2489                }
2490           }
2491      }
2492 }
2493
2494 static Evas_Object_Textblock_Line *
2495 _find_layout_line_num(const Evas_Object *obj, int line)
2496 {
2497    Evas_Object_Textblock_Line *ln;
2498    Evas_Object_Textblock *o;
2499
2500    o = (Evas_Object_Textblock *)(obj->object_data);
2501    EINA_INLIST_FOREACH(o->lines, ln)
2502      {
2503         if (ln->line_no == line) return ln;
2504      }
2505    return NULL;
2506 }
2507
2508 /**
2509  * Adds a textblock to the given evas.
2510  * @param   e The given evas.
2511  * @return  The new textblock object.
2512  */
2513 EAPI Evas_Object *
2514 evas_object_textblock_add(Evas *e)
2515 {
2516    Evas_Object *obj;
2517
2518    MAGIC_CHECK(e, Evas, MAGIC_EVAS);
2519    return NULL;
2520    MAGIC_CHECK_END();
2521    obj = evas_object_new(e);
2522    evas_object_textblock_init(obj);
2523    evas_object_inject(obj, e);
2524    return obj;
2525 }
2526
2527 /**
2528  * Creates a new textblock style.
2529  * @return  The new textblock style.
2530  */
2531 EAPI Evas_Textblock_Style *
2532 evas_textblock_style_new(void)
2533 {
2534    Evas_Textblock_Style *ts;
2535
2536    ts = calloc(1, sizeof(Evas_Textblock_Style));
2537    return ts;
2538 }
2539
2540 /**
2541  * Destroys a textblock style.
2542  * @param ts The textblock style to free.
2543  */
2544 EAPI void
2545 evas_textblock_style_free(Evas_Textblock_Style *ts)
2546 {
2547    if (!ts) return;
2548    if (ts->objects)
2549      {
2550         ts->delete_me = 1;
2551         return;
2552      }
2553    _style_clear(ts);
2554    free(ts);
2555 }
2556
2557 /**
2558  * to be documented.
2559  * @param ts  to be documented.
2560  * @param text  to be documented.
2561  * @return Returns no value.
2562  */
2563 EAPI void
2564 evas_textblock_style_set(Evas_Textblock_Style *ts, const char *text)
2565 {
2566    Eina_List *l;
2567    Evas_Object *obj;
2568
2569    if (!ts) return;
2570
2571    EINA_LIST_FOREACH(ts->objects, l, obj)
2572      {
2573         Evas_Object_Textblock *o;
2574
2575         o = (Evas_Object_Textblock *)(obj->object_data);
2576         if (o->markup_text)
2577           {
2578              free(o->markup_text);
2579              o->markup_text = NULL;
2580              evas_object_textblock_text_markup_get(obj);
2581           }
2582      }
2583
2584    _style_clear(ts);
2585    if (text) ts->style_text = strdup(text);
2586
2587    if (ts->style_text)
2588      {
2589         // format MUST be KEY='VALUE'[KEY='VALUE']...
2590         char *p;
2591         char *key_start, *key_stop, *val_start, *val_stop;
2592
2593         key_start = key_stop = val_start = val_stop = NULL;
2594         p = ts->style_text;
2595         while (*p)
2596           {
2597              if (!key_start)
2598                {
2599                   if (!isspace(*p))
2600                     key_start = p;
2601                }
2602              else if (!key_stop)
2603                {
2604                   if ((*p == '=') || (isspace(*p)))
2605                     key_stop = p;
2606                }
2607              else if (!val_start)
2608                {
2609                   if (((*p) == '\'') && (*(p + 1)))
2610                     val_start = p + 1;
2611                }
2612              else if (!val_stop)
2613                {
2614                   if (((*p) == '\'') && (p > ts->style_text) && (p[-1] != '\\'))
2615                     val_stop = p;
2616                }
2617              if ((key_start) && (key_stop) && (val_start) && (val_stop))
2618                {
2619                   char *tags, *replaces;
2620                   Evas_Object_Style_Tag *tag;
2621                   size_t tag_len = key_stop - key_start;
2622                   size_t replace_len = val_stop - val_start;
2623
2624                   tags = malloc(tag_len + 1);
2625                   if (tags)
2626                     {
2627                        memcpy(tags, key_start, tag_len);
2628                        tags[tag_len] = 0;
2629                     }
2630
2631                   replaces = malloc(replace_len + 1);
2632                   if (replaces)
2633                     {
2634                        memcpy(replaces, val_start, replace_len);
2635                        replaces[replace_len] = 0;
2636                     }
2637                   if ((tags) && (replaces))
2638                     {
2639                        if (!strcmp(tags, "DEFAULT"))
2640                          {
2641                             ts->default_tag = replaces;
2642                             free(tags);
2643                          }
2644                        else
2645                          {
2646                             tag = calloc(1, sizeof(Evas_Object_Style_Tag));
2647                             if (tag)
2648                               {
2649                                  tag->tag = tags;
2650                                  tag->replace = replaces;
2651                                  tag->tag_len = tag_len;
2652                                  tag->replace_len = replace_len;
2653                                  ts->tags = (Evas_Object_Style_Tag *)eina_inlist_append(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
2654                               }
2655                             else
2656                               {
2657                                  free(tags);
2658                                  free(replaces);
2659                               }
2660                          }
2661                     }
2662                   else
2663                     {
2664                        if (tags) free(tags);
2665                        if (replaces) free(replaces);
2666                     }
2667                   key_start = key_stop = val_start = val_stop = NULL;
2668                }
2669              p++;
2670           }
2671      }
2672
2673    EINA_LIST_FOREACH(ts->objects, l, obj)
2674      {
2675         Evas_Object_Textblock *o;
2676
2677         o = (Evas_Object_Textblock *)(obj->object_data);
2678         if (o->markup_text)
2679           {
2680              char *m;
2681
2682              m = strdup(o->markup_text);
2683              if (m)
2684                {
2685                   evas_object_textblock_text_markup_set(obj, m);
2686                   free(m);
2687                }
2688           }
2689      }
2690 }
2691
2692 /**
2693  * to be documented.
2694  * @param ts  to be documented.
2695  * @return to be documented.
2696  */
2697 EAPI const char *
2698 evas_textblock_style_get(const Evas_Textblock_Style *ts)
2699 {
2700    if (!ts) return NULL;
2701    return ts->style_text;
2702 }
2703
2704 /* textblock styles */
2705 /**
2706  * to be documented.
2707  * @param obj to be documented.
2708  * @param ts  to be documented.
2709  * @return Returns no value.
2710  */
2711 EAPI void
2712 evas_object_textblock_style_set(Evas_Object *obj, Evas_Textblock_Style *ts)
2713 {
2714    TB_HEAD();
2715    if (ts == o->style) return;
2716    if ((ts) && (ts->delete_me)) return;
2717    if (o->markup_text)
2718      {
2719         if (o->style)
2720           {
2721              free(o->markup_text);
2722              o->markup_text = NULL;
2723              evas_object_textblock_text_markup_get(obj);
2724           }
2725      }
2726    if (o->style)
2727      {
2728         Evas_Textblock_Style *old_ts;
2729
2730         old_ts = o->style;
2731         old_ts->objects = eina_list_remove(old_ts->objects, obj);
2732         if ((old_ts->delete_me) && (!old_ts->objects))
2733           evas_textblock_style_free(old_ts);
2734      }
2735    if (ts)
2736      {
2737         ts->objects = eina_list_append(ts->objects, obj);
2738         o->style = ts;
2739      }
2740    else
2741      {
2742         o->style = NULL;
2743      }
2744
2745    o->formatted.valid = 0;
2746    o->native.valid = 0;
2747    o->changed = 1;
2748    if (o->markup_text)
2749      {
2750         free(o->markup_text);
2751         o->markup_text = NULL;
2752      }
2753    evas_object_change(obj);
2754 }
2755
2756 /**
2757  * to be documented.
2758  * @param obj  to be documented.
2759  * @return to be documented.
2760  */
2761 EAPI const Evas_Textblock_Style *
2762 evas_object_textblock_style_get(const Evas_Object *obj)
2763 {
2764    TB_HEAD_RETURN(NULL);
2765    return o->style;
2766 }
2767
2768 /**
2769  * @brief Set the "replacement character" to use for the given textblock object.
2770  *
2771  * @param obj The given textblock object.
2772  * @param ch The charset name.
2773  */
2774 EAPI void
2775 evas_object_textblock_replace_char_set(Evas_Object *obj, const char *ch)
2776 {
2777    TB_HEAD();
2778    if (o->repch) eina_stringshare_del(o->repch);
2779    if (ch) o->repch = eina_stringshare_add(ch);
2780    else o->repch = NULL;
2781    o->formatted.valid = 0;
2782    o->native.valid = 0;
2783    o->changed = 1;
2784    if (o->markup_text)
2785      {
2786         free(o->markup_text);
2787         o->markup_text = NULL;
2788      }
2789    evas_object_change(obj);
2790 }
2791
2792 /**
2793  * @brief Get the "replacement character" for given textblock object. Returns
2794  * NULL if no replacement character is in use.
2795  *
2796  * @param obj The given textblock object
2797  * @return replacement character or NULL
2798  */
2799 EAPI const char *
2800 evas_object_textblock_replace_char_get(Evas_Object *obj)
2801 {
2802    TB_HEAD_RETURN(NULL);
2803    return o->repch;
2804 }
2805
2806
2807 static inline void
2808 _advance_after_end_of_string(const char **p_buf)
2809 {
2810    while (**p_buf != 0) (*p_buf)++;
2811    (*p_buf)++;
2812 }
2813
2814 static inline int
2815 _is_eq_and_advance(const char *s, const char *s_end,
2816                    const char **p_m, const char *m_end)
2817 {
2818    for (;((s < s_end) && (*p_m < m_end)); s++, (*p_m)++)
2819      {
2820         if (*s != **p_m)
2821           {
2822              _advance_after_end_of_string(p_m);
2823              return 0;
2824           }
2825      }
2826
2827    if (*p_m < m_end)
2828      _advance_after_end_of_string(p_m);
2829
2830    return s == s_end;
2831 }
2832
2833 static inline const char *
2834 _escaped_char_match(const char *s, int *adv)
2835 {
2836    const char *map_itr, *map_end, *mc, *sc;
2837
2838    map_itr = escape_strings;
2839    map_end = map_itr + sizeof(escape_strings);
2840
2841    while (map_itr < map_end)
2842      {
2843         const char *escape;
2844         int match;
2845
2846         escape = map_itr;
2847         _advance_after_end_of_string(&map_itr);
2848         if (map_itr >= map_end) break;
2849
2850         mc = map_itr;
2851         sc = s;
2852         match = 1;
2853         while ((*mc) && (*sc))
2854           {
2855              if ((unsigned char)*sc < (unsigned char)*mc) return NULL;
2856              if (*sc != *mc) match = 0;
2857              mc++;
2858              sc++;
2859           }
2860         if (match)
2861           {
2862              *adv = mc - map_itr;
2863              return escape;
2864           }
2865         _advance_after_end_of_string(&map_itr);
2866      }
2867    return NULL;
2868 }
2869
2870 static inline const char *
2871 _escaped_char_get(const char *s, const char *s_end)
2872 {
2873    const char *map_itr, *map_end;
2874
2875    map_itr = escape_strings;
2876    map_end = map_itr + sizeof(escape_strings);
2877
2878    while (map_itr < map_end)
2879      {
2880         if (_is_eq_and_advance(s, s_end, &map_itr, map_end))
2881           return map_itr;
2882         if (map_itr < map_end)
2883           _advance_after_end_of_string(&map_itr);
2884      }
2885    return NULL;
2886 }
2887
2888 /**
2889  * to be documented.
2890  * @param escape to be documented.
2891  * @return to be documented.
2892  */
2893 EAPI const char *
2894 evas_textblock_escape_string_get(const char *escape)
2895 {
2896    /* &amp; -> & */
2897    return _escaped_char_get(escape, escape + strlen(escape));
2898 }
2899
2900 /**
2901  * to be documented.
2902  * @param escape_start to be documented.
2903  * @param escape_end to be documented.
2904  * @return to be documented.
2905  */
2906 EAPI const char *
2907 evas_textblock_escape_string_range_get(const char *escape_start, const char *escape_end)
2908 {
2909    return _escaped_char_get(escape_start, escape_end);
2910 }
2911
2912 /**
2913  * to be documented.
2914  * @param string to be documented.
2915  * @param len_ret to be documented.
2916  * @return to be documented.
2917  */
2918 EAPI const char *
2919 evas_textblock_string_escape_get(const char *string, int *len_ret)
2920 {
2921    /* & -> &amp; */
2922    return _escaped_char_match(string, len_ret);
2923 }
2924
2925 static inline void
2926 _append_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
2927                      const char *s_end)
2928 {
2929    const char *escape;
2930
2931    escape = _escaped_char_get(s, s_end);
2932    if (escape)
2933      evas_textblock_cursor_text_append(cur, escape);
2934 }
2935
2936 static inline void
2937 _prepend_escaped_char(Evas_Textblock_Cursor *cur, const char *s,
2938                      const char *s_end)
2939 {
2940    const char *escape;
2941
2942    escape = _escaped_char_get(s, s_end);
2943    if (escape)
2944      evas_textblock_cursor_text_prepend(cur, escape);
2945 }
2946
2947 /**
2948  * to be documented.
2949  * @param obj  to be documented.
2950  * @param text to be documented.
2951  * @return Return no value.
2952  */
2953 EAPI void
2954 evas_object_textblock_text_markup_set(Evas_Object *obj, const char *text)
2955 {
2956    TB_HEAD();
2957    if ((text != o->markup_text) && (o->markup_text))
2958      {
2959         free(o->markup_text);
2960         o->markup_text = NULL;
2961      }
2962    _nodes_clear(obj);
2963    o->formatted.valid = 0;
2964    o->native.valid = 0;
2965    o->changed = 1;
2966    evas_object_change(obj);
2967    if (!o->style)
2968      {
2969         if (text != o->markup_text)
2970           {
2971              if (text) o->markup_text = strdup(text);
2972           }
2973         return;
2974      }
2975    evas_textblock_cursor_node_first(o->cursor);
2976    if (text)
2977      {
2978         char *s, *p;
2979         char *tag_start, *tag_end, *esc_start, *esc_end;
2980
2981         tag_start = tag_end = esc_start = esc_end = NULL;
2982         p = (char *)text;
2983         s = p;
2984         for (;;)
2985           {
2986              if ((*p == 0) ||
2987                  (tag_end) || (esc_end) ||
2988                  (tag_start) || (esc_start))
2989                {
2990                   if (tag_end)
2991                     {
2992                        char *ttag;
2993                        size_t ttag_len = tag_end - tag_start -1;
2994
2995                        ttag = malloc(ttag_len + 1);
2996                        if (ttag)
2997                          {
2998                             const char *match;
2999                             size_t replace_len;
3000
3001                             memcpy(ttag, tag_start + 1, ttag_len);
3002                             ttag[ttag_len] = 0;
3003                             match = _style_match_tag(o->style, ttag, ttag_len, &replace_len);
3004                             if (match)
3005                               evas_textblock_cursor_format_append(o->cursor, match);
3006                             else
3007                               {
3008                                  char *ttag2;
3009
3010                                  ttag2 = malloc(ttag_len + 2 + 1);
3011                                  if (ttag2)
3012                                    {
3013                                       if (ttag[0] == '/')
3014                                         {
3015                                            strcpy(ttag2, "- ");
3016                                            strcat(ttag2, ttag + 1);
3017                                         }
3018                                       else
3019                                         {
3020                                            strcpy(ttag2, "+ ");
3021                                            strcat(ttag2, ttag);
3022                                         }
3023                                       evas_textblock_cursor_format_append(o->cursor, ttag2);
3024                                       free(ttag2);
3025                                    }
3026                               }
3027                             free(ttag);
3028                          }
3029                        tag_start = tag_end = NULL;
3030                     }
3031                   else if (esc_end)
3032                     {
3033                        _append_escaped_char(o->cursor, esc_start, esc_end);
3034                        esc_start = esc_end = NULL;
3035                     }
3036                   else if (*p == 0)
3037                     {
3038                        _append_text_run(o, s, p);
3039                        s = NULL;
3040                     }
3041                   if (*p == 0)
3042                     break;
3043                }
3044              if (*p == '<')
3045                {
3046                   if (!esc_start)
3047                     {
3048                        tag_start = p;
3049                        tag_end = NULL;
3050                        _append_text_run(o, s, p);
3051                        s = NULL;
3052                     }
3053                }
3054              else if (*p == '>')
3055                {
3056                   if (tag_start)
3057                     {
3058                        tag_end = p;
3059                        s = p + 1;
3060                     }
3061                }
3062              else if (*p == '&')
3063                {
3064                   if (!tag_start)
3065                     {
3066                        esc_start = p;
3067                        esc_end = NULL;
3068                        _append_text_run(o, s, p);
3069                        s = NULL;
3070                     }
3071                }
3072              else if (*p == ';')
3073                {
3074                   if (esc_start)
3075                     {
3076                        esc_end = p;
3077                        s = p + 1;
3078                     }
3079                }
3080              p++;
3081           }
3082      }
3083      {
3084         Eina_List *l;
3085         Evas_Textblock_Cursor *data;
3086
3087         evas_textblock_cursor_node_first(o->cursor);
3088         EINA_LIST_FOREACH(o->cursors, l, data)
3089           evas_textblock_cursor_node_first(data);
3090      }
3091 }
3092
3093 /**
3094  * to be documented.
3095  * @param cur  to be documented.
3096  * @param text to be documented.
3097  * @return Return no value.
3098  */
3099 EAPI void
3100 evas_object_textblock_text_markup_prepend(Evas_Textblock_Cursor *cur, const char *text)
3101 {
3102    Evas_Object_Textblock *o;
3103
3104    if (!cur) return;
3105    o = (Evas_Object_Textblock *)(cur->obj->object_data);
3106    if (o->markup_text)
3107      {
3108         free(o->markup_text);
3109         o->markup_text = NULL;
3110      }
3111    o->formatted.valid = 0;
3112    o->native.valid = 0;
3113    o->changed = 1;
3114    evas_object_change(cur->obj);
3115    if (!o->style) return;
3116    if (text)
3117      {
3118         char *s, *p;
3119         char *tag_start, *tag_end, *esc_start, *esc_end;
3120
3121         tag_start = tag_end = esc_start = esc_end = NULL;
3122         p = (char *)text;
3123         s = p;
3124         for (;;)
3125           {
3126              if ((*p == 0) ||
3127                  (tag_end) || (esc_end) ||
3128                  (tag_start) || (esc_start))
3129                {
3130                   if (tag_end)
3131                     {
3132                        char *ttag;
3133                        size_t ttag_len = tag_end - tag_start - 1;
3134
3135                        ttag = malloc(ttag_len + 1);
3136                        if (ttag)
3137                          {
3138                             const char *match;
3139                             size_t replace_len;
3140
3141                             strncpy(ttag, tag_start + 1, ttag_len);
3142                             ttag[ttag_len] = 0;
3143                             match = _style_match_tag(o->style, ttag, ttag_len, &replace_len);
3144                             if (match)
3145                               evas_textblock_cursor_format_prepend(cur, match);
3146                             else
3147                               {
3148                                  char *ttag2;
3149
3150                                  ttag2 = malloc(ttag_len + 2 + 1);
3151                                  if (ttag2)
3152                                    {
3153                                       if (ttag[0] == '/')
3154                                         {
3155                                            strcpy(ttag2, "- ");
3156                                            strcat(ttag2, ttag + 1);
3157                                         }
3158                                       else
3159                                         {
3160                                            strcpy(ttag2, "+ ");
3161                                            strcat(ttag2, ttag);
3162                                         }
3163                                       evas_textblock_cursor_format_prepend(o->cursor, ttag2);
3164                                       free(ttag2);
3165                                    }
3166                               }
3167                             free(ttag);
3168                          }
3169                        tag_start = tag_end = NULL;
3170                     }
3171                   else if (esc_end)
3172                     {
3173                        _prepend_escaped_char(cur, esc_start, esc_end);
3174                        esc_start = esc_end = NULL;
3175                     }
3176                   else if (*p == 0)
3177                     {
3178                        _prepend_text_run(o, s, p);
3179                        s = NULL;
3180                     }
3181                   if (*p == 0)
3182                     break;
3183                }
3184              if (*p == '<')
3185                {
3186                   if (!esc_start)
3187                     {
3188                        tag_start = p;
3189                        tag_end = NULL;
3190                        _prepend_text_run(o, s, p);
3191                        s = NULL;
3192                     }
3193                }
3194              else if (*p == '>')
3195                {
3196                   if (tag_start)
3197                     {
3198                        tag_end = p;
3199                        s = p + 1;
3200                     }
3201                }
3202              else if (*p == '&')
3203                {
3204                   if (!tag_start)
3205                     {
3206                        esc_start = p;
3207                        esc_end = NULL;
3208                        _prepend_text_run(o, s, p);
3209                        s = NULL;
3210                     }
3211                }
3212              else if (*p == ';')
3213                {
3214                   if (esc_start)
3215                     {
3216                        esc_end = p;
3217                        s = p + 1;
3218                     }
3219                }
3220              p++;
3221           }
3222      }
3223 }
3224
3225 /**
3226  * to be documented.
3227  * @param obj  to be documented.
3228  * @return to be documented.
3229  */
3230 EAPI const char *
3231 evas_object_textblock_text_markup_get(const Evas_Object *obj)
3232 {
3233    Evas_Object_Textblock_Node *n;
3234    Eina_Strbuf *txt = NULL;
3235
3236    TB_HEAD_RETURN(NULL);
3237    if (o->markup_text) return(o->markup_text);
3238    txt = eina_strbuf_new();
3239    EINA_INLIST_FOREACH(o->nodes, n)
3240      {
3241         size_t replace_len = eina_strbuf_length_get(n->text);
3242         if ((n->type == NODE_FORMAT) && replace_len)
3243           {
3244              size_t tag_len;
3245              const char *tag = _style_match_replace(o->style, eina_strbuf_string_get(n->text), replace_len, &tag_len);
3246              eina_strbuf_append_char(txt, '<');
3247              if (tag)
3248                {
3249                   // FIXME: need to escape
3250                   eina_strbuf_append_length(txt, tag, tag_len);
3251                }
3252              else
3253                {
3254                   const char *s;
3255                   int push = 0;
3256                   int pop = 0;
3257
3258                   // FIXME: need to escape
3259                   s = eina_strbuf_string_get(n->text);
3260                   if (*s == '+') push = 1;
3261                   if (*s == '-') pop = 1;
3262                   while ((*s == ' ') || (*s == '+') || (*s == '-')) s++;
3263                   if (pop) eina_strbuf_append_char(txt, '/');
3264                   if (push) eina_strbuf_append(txt, "+ ");
3265                   eina_strbuf_append(txt, s);
3266                }
3267              eina_strbuf_append_char(txt, '>');
3268           }
3269         else if ((n->type == NODE_TEXT) && eina_strbuf_length_get(n->text))
3270           {
3271              const char *p = eina_strbuf_string_get(n->text);
3272
3273              while (*p)
3274                {
3275                   const char *escape;
3276                   int adv;
3277
3278                   escape = _escaped_char_match(p, &adv);
3279                   if (escape)
3280                     {
3281                        p += adv;
3282                        eina_strbuf_append(txt, escape);
3283                     }
3284                   else
3285                     {
3286                        eina_strbuf_append_char(txt, *p);
3287                        p++;
3288                     }
3289                }
3290           }
3291      }
3292    o->markup_text = eina_strbuf_string_steal(txt);
3293    eina_strbuf_free(txt);
3294    return o->markup_text;
3295 }
3296
3297 /* cursors */
3298 /**
3299  * to be documented.
3300  * @param obj  to be documented.
3301  * @return to be documented.
3302  */
3303 EAPI const Evas_Textblock_Cursor *
3304 evas_object_textblock_cursor_get(const Evas_Object *obj)
3305 {
3306    TB_HEAD_RETURN(NULL);
3307    return o->cursor;
3308 }
3309
3310 /**
3311  * to be documented.
3312  * @param obj  to be documented.
3313  * @return to be documented.
3314  */
3315 EAPI Evas_Textblock_Cursor *
3316 evas_object_textblock_cursor_new(Evas_Object *obj)
3317 {
3318    Evas_Textblock_Cursor *cur;
3319
3320    TB_HEAD_RETURN(NULL);
3321    cur = calloc(1, sizeof(Evas_Textblock_Cursor));
3322    cur->obj = obj;
3323    cur->node = o->nodes;
3324    cur->pos = 0;
3325    cur->eol = 0;
3326    o->cursors = eina_list_append(o->cursors, cur);
3327    return cur;
3328 }
3329
3330 /**
3331  * to be documented.
3332  * @param cur  to be documented.
3333  * @return Returns no value.
3334  */
3335 EAPI void
3336 evas_textblock_cursor_free(Evas_Textblock_Cursor *cur)
3337 {
3338    Evas_Object_Textblock *o;
3339
3340    if (!cur) return;
3341    o = (Evas_Object_Textblock *)(cur->obj->object_data);
3342    if (cur == o->cursor) return;
3343    o->cursors = eina_list_remove(o->cursors, cur);
3344    free(cur);
3345 }
3346
3347 /**
3348  * to be documented.
3349  * @param cur  to be documented.
3350  * @return Returns no value.
3351  */
3352 EAPI void
3353 evas_textblock_cursor_node_first(Evas_Textblock_Cursor *cur)
3354 {
3355    Evas_Object_Textblock *o;
3356
3357    if (!cur) return;
3358    o = (Evas_Object_Textblock *)(cur->obj->object_data);
3359    cur->node = o->nodes;
3360    cur->pos = 0;
3361    cur->eol = 0;
3362 }
3363
3364 /**
3365  * to be documented.
3366  * @param cur  to be documented.
3367  * @return Returns no value.
3368  */
3369 EAPI void
3370 evas_textblock_cursor_node_last(Evas_Textblock_Cursor *cur)
3371 {
3372    Evas_Object_Textblock *o;
3373
3374    if (!cur) return;
3375    o = (Evas_Object_Textblock *)(cur->obj->object_data);
3376    if (o->nodes)
3377      {
3378         cur->node = (Evas_Object_Textblock_Node *)((EINA_INLIST_GET(o->nodes))->last);
3379         cur->pos = 0;
3380         cur->eol = 0;  // 1
3381         evas_textblock_cursor_char_last(cur);
3382      }
3383    else
3384      {
3385         cur->node = NULL;
3386         cur->pos = 0;
3387         cur->eol = 0; // 1
3388      }
3389 }
3390
3391 /**
3392  * to be documented.
3393  * @param cur  to be documented.
3394  * @return to be documented.
3395  */
3396 EAPI Eina_Bool
3397 evas_textblock_cursor_node_next(Evas_Textblock_Cursor *cur)
3398 {
3399    if (!cur) return EINA_FALSE;
3400    if (!cur->node) return EINA_FALSE;
3401    if ((EINA_INLIST_GET(cur->node))->next)
3402      {
3403         cur->node = (Evas_Object_Textblock_Node *)((EINA_INLIST_GET(cur->node))->next);
3404         cur->pos = 0;
3405         cur->eol = 0;
3406         return EINA_TRUE;
3407      }
3408    return EINA_FALSE;
3409 }
3410
3411 /**
3412  * to be documented.
3413  * @param cur  to be documented.
3414  * @return to be documented.
3415  */
3416 EAPI Eina_Bool
3417 evas_textblock_cursor_node_prev(Evas_Textblock_Cursor *cur)
3418 {
3419    if (!cur) return EINA_FALSE;
3420    if (!cur->node) return EINA_FALSE;
3421    if ((EINA_INLIST_GET(cur->node))->prev)
3422      {
3423         cur->node = (Evas_Object_Textblock_Node *)((EINA_INLIST_GET(cur->node))->prev);
3424         evas_textblock_cursor_char_last(cur);
3425         return EINA_TRUE;
3426      }
3427    return EINA_FALSE;
3428 }
3429
3430 /**
3431  * to be documented.
3432  * @param cur  to be documented.
3433  * @return to be documented.
3434  */
3435 EAPI Eina_Bool
3436 evas_textblock_cursor_char_next(Evas_Textblock_Cursor *cur)
3437 {
3438    int index, ch;
3439
3440    if (!cur) return EINA_FALSE;
3441    if (!cur->node) return EINA_FALSE;
3442    if (cur->node->type == NODE_FORMAT) return EINA_FALSE;
3443    if (!eina_strbuf_length_get(cur->node->text)) return EINA_FALSE;
3444    index = cur->pos;
3445
3446    if (cur->node->type == NODE_TEXT)
3447      {
3448         Evas_Object_Textblock_Line *ln = NULL;
3449         Evas_Object_Textblock_Item *it = NULL;
3450         int pos;
3451
3452         _find_layout_item_line_match(cur->obj, cur->node, cur->pos, cur->eol, &ln, &it);
3453         if (it)
3454           {
3455              pos = cur->pos - it->source_pos;
3456              if (pos <= 0) index -= pos;
3457           }
3458         else
3459           printf("TB: 'it' not found\n");
3460      }
3461
3462    ch = evas_common_font_utf8_get_next((unsigned char *)eina_strbuf_string_get(cur->node->text), &index);
3463    if ((ch == 0) || (index < 0)) return EINA_FALSE;
3464    if (eina_strbuf_string_get(cur->node->text)[index] == 0) return EINA_FALSE;
3465    cur->pos = index;
3466    cur->eol = 0; // 1
3467    return EINA_TRUE;
3468 }
3469
3470 /**
3471  * to be documented.
3472  * @param cur  to be documented.
3473  * @return to be documented.
3474  */
3475 EAPI Eina_Bool
3476 evas_textblock_cursor_char_prev(Evas_Textblock_Cursor *cur)
3477 {
3478    int index;
3479    int at_end_of_line = 0;
3480    int at_start_of_line = 0;
3481
3482    if (!cur) return EINA_FALSE;
3483    if (!cur->node) return EINA_FALSE;
3484    if (cur->node->type == NODE_FORMAT) return EINA_FALSE;
3485    if (!eina_strbuf_length_get(cur->node->text)) return EINA_FALSE;
3486    index = cur->pos;
3487    if (index == 0) return EINA_FALSE;
3488
3489    // XXX: FIXME: determine at_end_of_line and at_start_of_line
3490
3491    if (cur->node->type == NODE_TEXT)
3492      {
3493         Evas_Object_Textblock_Line *ln = NULL;
3494         Evas_Object_Textblock_Item *it = NULL;
3495         int pos;
3496
3497         _find_layout_item_line_match(cur->obj, cur->node, cur->pos, cur->eol, &ln, &it);
3498         if (it)
3499           {
3500              pos = cur->pos - it->source_pos;
3501              if (pos <= 0) at_start_of_line = 1;
3502              if (it->text)
3503                {
3504                   int plast;
3505
3506                   plast = evas_common_font_utf8_get_last((unsigned char *) it->text, strlen(it->text));
3507                   if ((index - it->source_pos) == plast) at_end_of_line = 1;
3508                }
3509           }
3510      }
3511
3512    if ((cur->eol) && (at_end_of_line))
3513      {
3514         cur->eol = 0;
3515         return EINA_TRUE;
3516      }
3517    evas_common_font_utf8_get_prev((unsigned char *)eina_strbuf_string_get(cur->node->text), &index);
3518    if (/*(ch == 0) || */(index < 0)) return EINA_FALSE;
3519    cur->pos = index;
3520    if (at_start_of_line)
3521      cur->eol =1;
3522    else
3523      cur->eol = 0;
3524    return EINA_TRUE;
3525 }
3526
3527 /**
3528  * to be documented.
3529  * @param cur  to be documented.
3530  * @return Returns no value.
3531  */
3532 EAPI void
3533 evas_textblock_cursor_char_first(Evas_Textblock_Cursor *cur)
3534 {
3535    if (!cur) return;
3536    cur->pos = 0;
3537    cur->eol = 0;
3538 }
3539
3540 /**
3541  * to be documented.
3542  * @param cur  to be documented.
3543  * @return Returns no value.
3544  */
3545 EAPI void
3546 evas_textblock_cursor_char_last(Evas_Textblock_Cursor *cur)
3547 {
3548    int index;
3549
3550    if (!cur) return;
3551    if (!cur->node) return;
3552    if (cur->node->type == NODE_FORMAT)
3553      {
3554         cur->pos = 0;
3555         return;
3556      }
3557    index = evas_common_font_utf8_get_last((unsigned char *)eina_strbuf_string_get(cur->node->text), eina_strbuf_length_get(cur->node->text));
3558    if (index < 0) cur->pos = 0;
3559    cur->pos = index;
3560    cur->eol = 0; // 1
3561 }
3562
3563 /**
3564  * to be documented.
3565  * @param cur  to be documented.
3566  * @return Returns no value.
3567  */
3568 EAPI void
3569 evas_textblock_cursor_line_first(Evas_Textblock_Cursor *cur)
3570 {
3571    Evas_Object_Textblock *o;
3572    Evas_Object_Textblock_Line *ln = NULL;
3573    Evas_Object_Textblock_Item *it = NULL;
3574    Evas_Object_Textblock_Format_Item *fi = NULL;
3575
3576    if (!cur) return;
3577    if (!cur->node) return;
3578    o = (Evas_Object_Textblock *)(cur->obj->object_data);
3579    if (!o->formatted.valid) _relayout(cur->obj);
3580    if (cur->node->type == NODE_FORMAT)
3581      _find_layout_format_item_line_match(cur->obj, cur->node, &ln, &fi);
3582    else
3583      _find_layout_item_line_match(cur->obj, cur->node, cur->pos, cur->eol, &ln, &it);
3584    if (!ln) return;
3585    cur->eol = 0;
3586    it = (Evas_Object_Textblock_Item *)ln->items;
3587    fi = (Evas_Object_Textblock_Format_Item *)ln->format_items;
3588    if ((it) && (fi))
3589      {
3590         if (it->x < fi->x) fi = NULL;
3591         else it = NULL;
3592      }
3593    if (it)
3594      {
3595         cur->pos = it->source_pos;
3596         cur->node = it->source_node;
3597      }
3598    else if (fi)
3599      {
3600         cur->pos = 0;
3601         cur->node = fi->source_node;
3602      }
3603 }
3604
3605 /**
3606  * to be documented.
3607  * @param cur  to be documented.
3608  * @return Returns no value.
3609  */
3610 EAPI void
3611 evas_textblock_cursor_line_last(Evas_Textblock_Cursor *cur)
3612 {
3613    Evas_Object_Textblock *o;
3614    Evas_Object_Textblock_Line *ln = NULL;
3615    Evas_Object_Textblock_Item *it = NULL;
3616    Evas_Object_Textblock_Format_Item *fi = NULL;
3617
3618    if (!cur) return;
3619    if (!cur->node) return;
3620    o = (Evas_Object_Textblock *)(cur->obj->object_data);
3621    if (!o->formatted.valid) _relayout(cur->obj);
3622 // kills "click below text" and up/downm arrow. disable   
3623 //   cur->eol = 1;
3624    if (cur->node->type == NODE_FORMAT)
3625      _find_layout_format_item_line_match(cur->obj, cur->node, &ln, &fi);
3626    else
3627      _find_layout_item_line_match(cur->obj, cur->node, cur->pos, cur->eol, &ln, &it);
3628    if (!ln) return;
3629    if (ln->items)
3630      it = (Evas_Object_Textblock_Item *)((EINA_INLIST_GET(ln->items))->last);
3631    else
3632      it = NULL;
3633    if (ln->format_items)
3634      fi = (Evas_Object_Textblock_Format_Item *)((EINA_INLIST_GET(ln->format_items))->last);
3635    else
3636      fi = NULL;
3637    if ((it) && (fi))
3638      {
3639         if ((it->x + it->w) > (fi->x + fi->w)) fi = NULL;
3640         else it = NULL;
3641      }
3642    if (it)
3643      {
3644         int index;
3645
3646         cur->pos = it->source_pos;
3647         cur->node = it->source_node;
3648         index = evas_common_font_utf8_get_last((unsigned char *)it->text, strlen(it->text));
3649         if ((index >= 0) && (it->text[0] != 0))
3650           evas_common_font_utf8_get_next((unsigned char *)(it->text), &index);
3651         if (index >= 0) cur->pos += index;
3652      }
3653    else if (fi)
3654      {
3655         cur->pos = 0;
3656         cur->eol = 0;
3657         cur->node = fi->source_node;
3658      }
3659 }
3660
3661 /**
3662  * to be documented.
3663  * @param cur  to be documented.
3664  * @return to be documented.
3665  */
3666 EAPI int
3667 evas_textblock_cursor_pos_get(const Evas_Textblock_Cursor *cur)
3668 {
3669    if (!cur) return -1;
3670    return cur->pos;
3671 }
3672
3673 /**
3674  * to be documented.
3675  * @param cur to be documented.
3676  * @param pos to be documented.
3677  */
3678 EAPI void
3679 evas_textblock_cursor_pos_set(Evas_Textblock_Cursor *cur, int pos)
3680 {
3681    unsigned int len;
3682
3683    if (!cur) return;
3684    if (!cur->node) return;
3685    if (cur->node->type == NODE_FORMAT) pos = 0;
3686    len = eina_strbuf_length_get(cur->node->text);
3687    if (pos < 0) pos = 0;
3688    else if (pos > len) pos = len;
3689    cur->pos = pos;
3690    cur->eol = 0;
3691 }
3692
3693 /**
3694  * to be documented.
3695  * @param cur to be documented.
3696  * @param line to be documented.
3697  * @return to be documented.
3698  */
3699 EAPI Eina_Bool
3700 evas_textblock_cursor_line_set(Evas_Textblock_Cursor *cur, int line)
3701 {
3702    Evas_Object_Textblock *o;
3703    Evas_Object_Textblock_Line *ln;
3704    Evas_Object_Textblock_Item *it;
3705    Evas_Object_Textblock_Format_Item *fi;
3706
3707    if (!cur) return EINA_FALSE;
3708    o = (Evas_Object_Textblock *)(cur->obj->object_data);
3709    if (!o->formatted.valid) _relayout(cur->obj);
3710
3711    ln = _find_layout_line_num(cur->obj, line);
3712    if (!ln) return EINA_FALSE;
3713    it = (Evas_Object_Textblock_Item *)ln->items;
3714    fi = (Evas_Object_Textblock_Format_Item *)ln->format_items;
3715    if ((it) && (fi))
3716      {
3717         if (it->x < fi->x) fi = NULL;
3718         else it = NULL;
3719      }
3720    if (it)
3721      {
3722         cur->pos = it->source_pos;
3723         cur->eol = 0;
3724         cur->node = it->source_node;
3725      }
3726    else if (fi)
3727      {
3728         cur->pos = 0;
3729         cur->eol = 0;
3730         cur->node = fi->source_node;
3731      }
3732    else
3733      {
3734         cur->pos = 0;
3735         cur->eol = 0;
3736         cur->node = o->nodes;
3737      }
3738    return EINA_TRUE;
3739 }
3740
3741 /**
3742  * to be documented.
3743  * @param cur1 to be documented.
3744  * @param cur2 to be documented.
3745  * @return to be documented.
3746  */
3747 EAPI int
3748 evas_textblock_cursor_compare(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
3749 {
3750    Eina_Inlist *l1, *l2;
3751
3752    if (!cur1) return 0;
3753    if (!cur2) return 0;
3754    if (cur1->obj != cur2->obj) return 0;
3755    if ((!cur1->node) || (!cur2->node)) return 0;
3756    if (cur1->node == cur2->node)
3757      {
3758         if (cur1->pos < cur2->pos) return -1; /* cur1 < cur2 */
3759         else if (cur1->pos > cur2->pos) return 1; /* cur2 < cur1 */
3760         if ((cur1->eol) == (cur1->eol)) return 0; /* cur1 == cur2 */
3761         if (cur1->eol) return 1; /* cur2 < cur1 */
3762         return -1; /* cur1 < cur2 */
3763      }
3764    for (l1 = EINA_INLIST_GET(cur1->node),
3765         l2 = EINA_INLIST_GET(cur1->node); (l1) || (l2);)
3766      {
3767         if (l1 == EINA_INLIST_GET(cur2->node)) return 1; /* cur2 < cur 1 */
3768         else if (l2 == EINA_INLIST_GET(cur2->node)) return -1; /* cur1 < cur 2 */
3769         else if (!l1) return -1; /* cur1 < cur 2 */
3770         else if (!l2) return 1; /* cur2 < cur 1 */
3771         if (l1) l1 = l1->prev;
3772         if (l2) l2 = l2->next;
3773      }
3774    return 0;
3775 }
3776
3777 /**
3778  * to be documented.
3779  * @param cur to be documented.
3780  * @param cur_dest to be documented.
3781  * @return Returns no value.
3782  */
3783 EAPI void
3784 evas_textblock_cursor_copy(const Evas_Textblock_Cursor *cur, Evas_Textblock_Cursor *cur_dest)
3785 {
3786    if (!cur) return;
3787    if (!cur_dest) return;
3788    if (cur->obj != cur_dest->obj) return;
3789    cur_dest->pos = cur->pos;
3790    cur_dest->node = cur->node;
3791    cur_dest->eol = cur->eol;
3792 }
3793
3794
3795 /* text controls */
3796 /**
3797  * to be documented.
3798  * @param cur to be documented.
3799  * @param text to be documented.
3800  * @return Returns no value.
3801  */
3802 EAPI void
3803 evas_textblock_cursor_text_append(Evas_Textblock_Cursor *cur, const char *text)
3804 {
3805    Evas_Object_Textblock *o;
3806    Evas_Object_Textblock_Node *n, *nrel;
3807    int index, ch;
3808
3809    if (!cur) return;
3810    o = (Evas_Object_Textblock *)(cur->obj->object_data);
3811    if (text)
3812      {
3813         Eina_List *l;
3814         Evas_Textblock_Cursor *data;
3815
3816         if (cur != o->cursor)
3817           {
3818              if (cur->node == o->cursor->node)
3819                {
3820                   if (o->cursor->pos > cur->pos)
3821                     {
3822                        o->cursor->pos += strlen(text);
3823                     }
3824                }
3825           }
3826         EINA_LIST_FOREACH(o->cursors, l, data)
3827           {
3828              if (data != cur)
3829                {
3830                   if (cur->node == data->node)
3831                     {
3832                        if (data->pos > cur->pos)
3833                          {
3834                             data->pos += strlen(text);
3835                          }
3836                     }
3837                }
3838           }
3839      }
3840    n = cur->node;
3841    if ((!n) || (n->type == NODE_FORMAT))
3842      {
3843         nrel = n;
3844         n = calloc(1, sizeof(Evas_Object_Textblock_Node));
3845         n->type = NODE_TEXT;
3846         n->text = eina_strbuf_new();
3847         if (nrel)
3848           o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_append_relative(EINA_INLIST_GET(o->nodes),
3849                                                                                EINA_INLIST_GET(n),
3850                                                                                EINA_INLIST_GET(nrel));
3851         else
3852           o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_append(EINA_INLIST_GET(o->nodes), EINA_INLIST_GET(n));
3853      }
3854    cur->node = n;
3855    index = cur->pos;
3856    if (eina_strbuf_length_get(n->text))
3857      {
3858         ch = evas_common_font_utf8_get_next((unsigned char *)eina_strbuf_string_get(n->text), &index);
3859         if (ch != 0)
3860           {
3861              cur->pos = index;
3862           }
3863      }
3864    if (cur->pos >= (eina_strbuf_length_get(n->text) - 1))
3865      eina_strbuf_append(n->text, (char *)text);
3866    else
3867      eina_strbuf_insert(n->text, (char *)text, cur->pos);
3868 // XXX: This makes no sense?
3869    if (text)
3870      {
3871         cur->pos += strlen(text);
3872      }
3873    o->formatted.valid = 0;
3874    o->native.valid = 0;
3875    o->changed = 1;
3876    if (o->markup_text)
3877      {
3878         free(o->markup_text);
3879         o->markup_text = NULL;
3880      }
3881    _nodes_adjacent_merge(cur->obj, n);
3882    evas_object_change(cur->obj);
3883 }
3884
3885 /**
3886  * to be documented.
3887  * @param cur to be documented.
3888  * @param text to be documented.
3889  * @return Returns no value.
3890  */
3891 EAPI void
3892 evas_textblock_cursor_text_prepend(Evas_Textblock_Cursor *cur, const char *text)
3893 {
3894    Evas_Object_Textblock *o;
3895    Evas_Object_Textblock_Node *n, *nrel;
3896
3897    if (!cur) return;
3898    o = (Evas_Object_Textblock *)(cur->obj->object_data);
3899      {
3900         Eina_List *l;
3901         Evas_Textblock_Cursor *data;
3902
3903         if (cur != o->cursor)
3904           {
3905              if (cur->node == o->cursor->node)
3906                {
3907                   if ((o->cursor->node) &&
3908                       (o->cursor->node->type == NODE_TEXT) &&
3909                       (o->cursor->pos >= cur->pos))
3910                     {
3911                        o->cursor->pos += strlen(text);
3912                     }
3913                }
3914           }
3915         EINA_LIST_FOREACH(o->cursors, l, data)
3916           {
3917              if (data != cur)
3918                {
3919                   if (cur->node == data->node)
3920                     {
3921                        if (data->node &&
3922                            (data->node->type == NODE_TEXT) &&
3923                            (data->pos >= cur->pos))
3924                          {
3925                             data->pos += strlen(text);
3926                          }
3927                     }
3928                }
3929           }
3930      }
3931    n = cur->node;
3932    if ((!n) || (n->type == NODE_FORMAT))
3933      {
3934         nrel = n;
3935         n = calloc(1, sizeof(Evas_Object_Textblock_Node));
3936         n->type = NODE_TEXT;
3937         n->text = eina_strbuf_new();
3938         if (nrel)
3939           o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_prepend_relative(EINA_INLIST_GET(o->nodes),
3940                                                                                 EINA_INLIST_GET(n),
3941                                                                                 EINA_INLIST_GET(nrel));
3942         else
3943           o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_prepend(EINA_INLIST_GET(o->nodes), EINA_INLIST_GET(n));
3944      }
3945    if (!n->text) n->text = eina_strbuf_new();
3946    cur->node = n;
3947    
3948    if (text)
3949      {
3950         if (cur->pos > (eina_strbuf_length_get(n->text) - 1))
3951           {
3952              eina_strbuf_append(n->text, (char *)text);
3953           }
3954         else
3955           {
3956              eina_strbuf_insert(n->text, (char *)text, cur->pos);
3957           }
3958         cur->pos += strlen(text);
3959      }
3960    o->formatted.valid = 0;
3961    o->native.valid = 0;
3962    o->changed = 1;
3963    if (o->markup_text)
3964      {
3965         free(o->markup_text);
3966         o->markup_text = NULL;
3967      }
3968    _nodes_adjacent_merge(cur->obj, n);
3969    evas_object_change(cur->obj);
3970 }
3971
3972 /**
3973  * to be documented.
3974  * @param cur to be documented.
3975  * @param format to be documented.
3976  * @return Returns no value.
3977  */
3978 EAPI void
3979 evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, const char *format)
3980 {
3981    Evas_Object_Textblock *o;
3982    Evas_Object_Textblock_Node *n, *nc, *n2;
3983
3984    if (!cur) return;
3985    if ((!format) || (format[0] == 0)) return;
3986    o = (Evas_Object_Textblock *)(cur->obj->object_data);
3987    nc = cur->node;
3988    n = calloc(1, sizeof(Evas_Object_Textblock_Node));
3989    n->type = NODE_FORMAT;
3990    n->text = eina_strbuf_new();
3991    eina_strbuf_append(n->text, format);
3992    if (!nc)
3993      {
3994         o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_append(EINA_INLIST_GET(o->nodes), EINA_INLIST_GET(n));
3995      }
3996    else if (nc->type == NODE_FORMAT)
3997      {
3998         o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_append_relative(EINA_INLIST_GET(o->nodes),
3999                                                                              EINA_INLIST_GET(n),
4000                                                                              EINA_INLIST_GET(nc));
4001      }
4002    else if (nc->type == NODE_TEXT)
4003      {
4004         int index, ch = 0;
4005
4006         index = cur->pos;
4007         if (eina_strbuf_length_get(nc->text))
4008           {
4009              ch = evas_common_font_utf8_get_next((unsigned char *)eina_strbuf_string_get(nc->text), &index);
4010              if (ch != 0)
4011                cur->pos = index;
4012           }
4013         o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_append_relative(EINA_INLIST_GET(o->nodes),
4014                                                                              EINA_INLIST_GET(n),
4015                                                                              EINA_INLIST_GET(nc));
4016         if ((ch != 0) && (cur->pos < eina_strbuf_length_get(nc->text)))
4017           {
4018              n2 = calloc(1, sizeof(Evas_Object_Textblock_Node));
4019              n2->type = NODE_TEXT;
4020              n2->text = eina_strbuf_new();
4021              eina_strbuf_append(n2->text, (eina_strbuf_string_get(nc->text) + cur->pos));
4022              o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_append_relative(EINA_INLIST_GET(o->nodes),
4023                                                                                   EINA_INLIST_GET(n2),
4024                                                                                   EINA_INLIST_GET(n));
4025
4026              eina_strbuf_remove(nc->text, cur->pos, eina_strbuf_length_get(nc->text));
4027           }
4028      }
4029    cur->node = n;
4030 // XXX: This makes no sense
4031    cur->pos = 0;
4032    o->formatted.valid = 0;
4033    o->native.valid = 0;
4034    o->changed = 1;
4035    if (o->markup_text)
4036      {
4037         free(o->markup_text);
4038         o->markup_text = NULL;
4039      }
4040    evas_object_change(cur->obj);
4041 }
4042
4043 /**
4044  * to be documented.
4045  * @param cur to be documented.
4046  * @param format to be documented.
4047  * @return Returns no value.
4048  */
4049 EAPI void
4050 evas_textblock_cursor_format_prepend(Evas_Textblock_Cursor *cur, const char *format)
4051 {
4052    Evas_Object_Textblock *o;
4053    Evas_Object_Textblock_Node *n, *nc, *n2;
4054
4055    if (!cur) return;
4056    if ((!format) || (format[0] == 0)) return;
4057    o = (Evas_Object_Textblock *)(cur->obj->object_data);
4058    nc = cur->node;
4059    n = calloc(1, sizeof(Evas_Object_Textblock_Node));
4060    n->type = NODE_FORMAT;
4061    n->text = eina_strbuf_new();
4062    eina_strbuf_append(n->text, format);
4063    if (!nc)
4064      {
4065         o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_prepend(EINA_INLIST_GET(o->nodes), EINA_INLIST_GET(n));
4066         cur->node = n;
4067         cur->pos = 0;
4068      }
4069    else if (nc->type == NODE_FORMAT)
4070      {
4071         o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_prepend_relative(EINA_INLIST_GET(o->nodes),
4072                                                                               EINA_INLIST_GET(n),
4073                                                                               EINA_INLIST_GET(nc));
4074         cur->node = nc;
4075         cur->pos = 0;
4076      }
4077    else if (nc->type == NODE_TEXT)
4078      {
4079         int len;
4080
4081         len = eina_strbuf_length_get(nc->text);
4082         if (cur->pos == 0)
4083           o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_prepend_relative(EINA_INLIST_GET(o->nodes),
4084                                                                                 EINA_INLIST_GET(n),
4085                                                                                 EINA_INLIST_GET(nc));
4086         else
4087           o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_append_relative(EINA_INLIST_GET(o->nodes),
4088                                                                                EINA_INLIST_GET(n),
4089                                                                                EINA_INLIST_GET(nc));
4090         if ((cur->pos < len) && (cur->pos != 0))
4091           {
4092              n2 = calloc(1, sizeof(Evas_Object_Textblock_Node));
4093              n2->type = NODE_TEXT;
4094              n2->text = eina_strbuf_new();
4095              eina_strbuf_append(n2->text, 
4096                                 (eina_strbuf_string_get(nc->text) + cur->pos));
4097              o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_append_relative(EINA_INLIST_GET(o->nodes),
4098                                                                                   EINA_INLIST_GET(n2),
4099                                                                                   EINA_INLIST_GET(n));
4100              eina_strbuf_remove(nc->text, cur->pos, eina_strbuf_length_get(nc->text));
4101              cur->node = n2;
4102              cur->pos = 0;
4103 //             cur->eol = 0;
4104           }
4105         else if (cur->pos == len)
4106           {
4107              if (EINA_INLIST_GET(n)->next)
4108                cur->node = EINA_INLIST_GET(n)->next;
4109              else
4110                cur->node = n;
4111              cur->pos = 0;
4112 //             cur->eol = 0;
4113           }
4114         else
4115           {
4116              cur->node = nc;
4117              cur->pos = 0;
4118 //             cur->eol = 0;
4119           }
4120      }
4121    o->formatted.valid = 0;
4122    o->native.valid = 0;
4123    o->changed = 1;
4124    if (o->markup_text)
4125      {
4126         free(o->markup_text);
4127         o->markup_text = NULL;
4128      }
4129    evas_object_change(cur->obj);
4130 }
4131
4132 /**
4133  * to be documented.
4134  * @param cur to be documented.
4135  * @return Returns no value.
4136  */
4137 EAPI void
4138 evas_textblock_cursor_node_delete(Evas_Textblock_Cursor *cur)
4139 {
4140    Evas_Object_Textblock *o;
4141    Evas_Object_Textblock_Node *n, *n2;
4142
4143    if (!cur) return;
4144    o = (Evas_Object_Textblock *)(cur->obj->object_data);
4145    n = cur->node;
4146    if (eina_strbuf_length_get(n->text) && (!strcmp(eina_strbuf_string_get(n->text), "\n")) &&
4147        (!(EINA_INLIST_GET(n))->next)) return;
4148    n2 = (Evas_Object_Textblock_Node *)((EINA_INLIST_GET(n))->next);
4149    if (n2)
4150      {
4151         cur->node = n2;
4152         cur->pos = 0;
4153      }
4154    else
4155      {
4156         n2 = (Evas_Object_Textblock_Node *)((EINA_INLIST_GET(n))->prev);
4157         cur->node = n2;
4158         cur->pos = 0;
4159         evas_textblock_cursor_char_last(cur);
4160      }
4161
4162      {
4163         Eina_List *l;
4164         Evas_Textblock_Cursor *data;
4165
4166         if (cur != o->cursor)
4167           {
4168              if (n == o->cursor->node)
4169                {
4170                   o->cursor->node = cur->node;
4171                   o->cursor->pos = cur->pos;
4172                   o->cursor->eol = cur->eol;
4173                }
4174           }
4175         EINA_LIST_FOREACH(o->cursors, l, data)
4176           {
4177              if (data != cur)
4178                {
4179                   if (n == data->node)
4180                     {
4181                        data->node = cur->node;
4182                        data->pos = cur->pos;
4183                        data->eol = cur->eol;
4184                     }
4185                }
4186           }
4187      }
4188
4189    o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_remove(EINA_INLIST_GET(o->nodes), EINA_INLIST_GET(n));
4190    if (n->text) eina_strbuf_free(n->text);
4191    free(n);
4192
4193    if (n2) _nodes_adjacent_merge(cur->obj, n2);
4194
4195    o->formatted.valid = 0;
4196    o->native.valid = 0;
4197    o->changed = 1;
4198    if (o->markup_text)
4199      {
4200         free(o->markup_text);
4201         o->markup_text = NULL;
4202      }
4203    evas_object_change(cur->obj);
4204 }
4205
4206 /**
4207  * to be documented.
4208  * @param cur to be documented.
4209  * @return Returns no value.
4210  */
4211 EAPI void
4212 evas_textblock_cursor_char_delete(Evas_Textblock_Cursor *cur)
4213 {
4214    Evas_Object_Textblock *o;
4215    Evas_Object_Textblock_Node *n, *n2;
4216    int chr, index, ppos;
4217
4218    if (!cur) return;
4219    o = (Evas_Object_Textblock *)(cur->obj->object_data);
4220    n = cur->node;
4221    if (n->type == NODE_FORMAT)
4222      {
4223         evas_textblock_cursor_node_delete(cur);
4224         return;
4225      }
4226    index = cur->pos;
4227    chr = evas_common_font_utf8_get_next((unsigned char *)eina_strbuf_string_get(n->text), &index);
4228    if (chr == 0) return;
4229    ppos = cur->pos;
4230    eina_strbuf_remove(n->text, cur->pos, index);
4231    if (!eina_strbuf_length_get(n->text))
4232      {
4233         evas_textblock_cursor_node_delete(cur);
4234         return;
4235      }
4236    if (cur->pos == eina_strbuf_length_get(n->text))
4237      {
4238         n2 = (Evas_Object_Textblock_Node *)((EINA_INLIST_GET(n))->next);
4239         if (n2)
4240           {
4241              cur->node = n2;
4242              cur->pos = 0;
4243           }
4244         else
4245           {
4246              cur->pos = 0;
4247              evas_textblock_cursor_char_last(cur);
4248           }
4249      }
4250
4251      {
4252         Eina_List *l;
4253         Evas_Textblock_Cursor *data;
4254
4255         if (cur != o->cursor)
4256           {
4257              if ((n == o->cursor->node) &&
4258                  (o->cursor->pos > ppos))
4259                {
4260                   o->cursor->pos -= (index - ppos);
4261                }
4262           }
4263         EINA_LIST_FOREACH(o->cursors, l, data)
4264           {
4265              if (data != cur)
4266                {
4267                   if ((n == data->node) &&
4268                       (data->pos > ppos))
4269                     {
4270                        data->pos -= (index - ppos);
4271                     }
4272                }
4273           }
4274      }
4275
4276    o->formatted.valid = 0;
4277    o->native.valid = 0;
4278    o->changed = 1;
4279    if (o->markup_text)
4280      {
4281         free(o->markup_text);
4282         o->markup_text = NULL;
4283      }
4284    evas_object_change(cur->obj);
4285 }
4286
4287 /**
4288  * to be documented.
4289  * @param cur1 to be documented.
4290  * @param cur2 to be documented.
4291  * @return Returns no value.
4292  */
4293 EAPI void
4294 evas_textblock_cursor_range_delete(Evas_Textblock_Cursor *cur1, Evas_Textblock_Cursor *cur2)
4295 {
4296    Evas_Object_Textblock *o;
4297    Evas_Object_Textblock_Node *n1, *n2, *n, *tn;
4298    int chr, index;
4299
4300    if (!cur1) return;
4301    if (!cur2) return;
4302    if (cur1->obj != cur2->obj) return;
4303    o = (Evas_Object_Textblock *)(cur1->obj->object_data);
4304    if (evas_textblock_cursor_compare(cur1, cur2) > 0)
4305      {
4306         Evas_Textblock_Cursor *tc;
4307
4308         tc = cur1;
4309         cur1 = cur2;
4310         cur2 = tc;
4311      }
4312    n1 = cur1->node;
4313    n2 = cur2->node;
4314    if ((!n1) || (!n2)) return;
4315    index = cur2->pos;
4316    chr = evas_common_font_utf8_get_next((unsigned char *)eina_strbuf_string_get(n2->text), &index);
4317 // XXX: why was this added? this stops sel to end and 
4318 //   if (chr == 0) return;
4319    if (n1 == n2)
4320      {
4321         if (n1->type == NODE_TEXT)
4322           {
4323             if (cur1->pos == cur2->pos)
4324                {
4325                   evas_textblock_cursor_char_delete(cur1);
4326                   evas_textblock_cursor_copy(cur1, cur2);
4327                   return;
4328                }
4329              eina_strbuf_remove(n1->text, cur1->pos, index);
4330              if (!eina_strbuf_length_get(n1->text))
4331                {
4332                   evas_textblock_cursor_node_delete(cur1);
4333                   evas_textblock_cursor_copy(cur1, cur2);
4334                   return;
4335                }
4336              if (cur1->pos >= eina_strbuf_length_get(n1->text))
4337                {
4338                   n2 = (Evas_Object_Textblock_Node *)((EINA_INLIST_GET(n1))->next);
4339                   if (n2)
4340                     {
4341                        cur1->node = n2;
4342                        cur1->pos = 0;
4343                     }
4344                   else
4345                     {
4346                        cur1->pos = 0;
4347                        evas_textblock_cursor_char_last(cur1);
4348                     }
4349                }
4350           }
4351         else
4352           evas_textblock_cursor_node_delete(cur1);
4353         evas_textblock_cursor_copy(cur1, cur2);
4354      }
4355    else
4356      {
4357         Eina_List *removes, *format_hump = NULL;
4358         Evas_Textblock_Cursor tcur;
4359         Eina_Inlist *l;
4360
4361         tcur.node = n2;
4362         tcur.pos = 0;
4363         index = cur2->pos;
4364         chr = evas_common_font_utf8_get_next((unsigned char *)eina_strbuf_string_get(n2->text), &index);
4365         if ((chr == 0) || (index >= eina_strbuf_length_get(n2->text)))
4366           {
4367              tcur.node = (Evas_Object_Textblock_Node *)(EINA_INLIST_GET(n2))->next;
4368              tcur.pos = 0;
4369              if (!tcur.node)
4370                {
4371                   if (cur1->pos != 0)
4372                     {
4373                        tcur.node = n1;
4374                        index = cur1->pos;
4375                        chr = evas_common_font_utf8_get_prev((unsigned char *)eina_strbuf_string_get(n2->text), &index);
4376                        tcur.pos = index;
4377                     }
4378                   else
4379                     {
4380                        tcur.node = (Evas_Object_Textblock_Node *)(EINA_INLIST_GET(n1))->prev;
4381                        if ((tcur.node) && (tcur.node->type == NODE_TEXT))
4382                          tcur.pos = evas_common_font_utf8_get_last((unsigned char *)eina_strbuf_string_get(tcur.node->text), eina_strbuf_length_get(tcur.node->text));
4383                        else
4384                          tcur.pos = 0;
4385                     }
4386                }
4387           }
4388         eina_strbuf_remove(n1->text, cur1->pos, eina_strbuf_length_get(n1->text));
4389         removes = NULL;
4390         for (l = (EINA_INLIST_GET(n1))->next; l != EINA_INLIST_GET(n2); l = l->next)
4391           removes = eina_list_append(removes, l);
4392         format_hump = NULL;
4393         if (n1->type == NODE_TEXT)
4394           {
4395              if (!eina_strbuf_length_get(n1->text))
4396                evas_textblock_cursor_node_delete(cur1);
4397           }
4398         else
4399           {
4400              if (eina_strbuf_length_get(n1->text) && (eina_strbuf_string_get(n1->text)[0] == '+'))
4401                format_hump = eina_list_append(format_hump, n1);
4402              else
4403                {
4404                   o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_remove(EINA_INLIST_GET(o->nodes), EINA_INLIST_GET(n1));
4405                   if (n1->text) eina_strbuf_free(n1->text);
4406                   free(n1);
4407                }
4408           }
4409         while (removes)
4410           {
4411              n = removes->data;
4412              if (n->type == NODE_TEXT)
4413                {
4414                   o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_remove(EINA_INLIST_GET(o->nodes), EINA_INLIST_GET(n));
4415                   if (n->text) eina_strbuf_free(n->text);
4416                   free(n);
4417                }
4418              else
4419                {
4420                   if (eina_strbuf_string_get(n->text)[0] == '+')
4421                     {
4422                        format_hump = eina_list_append(format_hump, n);
4423                     }
4424                   else if (eina_strbuf_string_get(n->text)[0] == '-')
4425                     {
4426                        tn = eina_list_data_get(eina_list_last(format_hump));
4427                        if (tn)
4428                          {
4429                             format_hump = eina_list_remove_list(format_hump, eina_list_last(format_hump));
4430                             o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_remove(EINA_INLIST_GET(o->nodes),
4431                                                                                         EINA_INLIST_GET(tn));
4432                             if (tn->text) eina_strbuf_free(tn->text);
4433                             free(tn);
4434                             o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_remove(EINA_INLIST_GET(o->nodes),
4435                                                                                         EINA_INLIST_GET(n));
4436                             if (n->text) eina_strbuf_free(n->text);
4437                             free(n);
4438                          }
4439                     }
4440                   else
4441                     {
4442                        o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_remove(EINA_INLIST_GET(o->nodes),
4443                                                                                    EINA_INLIST_GET(n));
4444                        if (n->text) eina_strbuf_free(n->text);
4445                        free(n);
4446                     }
4447                }
4448              removes = eina_list_remove_list(removes, removes);
4449           }
4450         if (n2->type == NODE_TEXT)
4451           {
4452              eina_strbuf_remove(n2->text, 0, index);
4453              if (!eina_strbuf_length_get(n2->text))
4454                evas_textblock_cursor_node_delete(cur2);
4455           }
4456         else
4457           {
4458              if (tcur.node == n2)
4459                {
4460                   if ((EINA_INLIST_GET(n2))->next)
4461                     {
4462                        tcur.node = (Evas_Object_Textblock_Node *) (EINA_INLIST_GET(n2))->next;
4463                        tcur.pos = 0;
4464                     }
4465                   else
4466                     {
4467                        tcur.node = (Evas_Object_Textblock_Node *) (EINA_INLIST_GET(n2))->next;
4468                        if (tcur.node)
4469                          {
4470                             if (tcur.node->type == NODE_TEXT)
4471                               tcur.pos = evas_common_font_utf8_get_last((unsigned char *)eina_strbuf_string_get(tcur.node->text), eina_strbuf_length_get(tcur.node->text));
4472                             else
4473                               tcur.pos = 0;
4474                          }
4475                     }
4476                }
4477              if (eina_strbuf_string_get(n2->text)[0] == '-')
4478                {
4479                   o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_remove(EINA_INLIST_GET(o->nodes),
4480                                                                               EINA_INLIST_GET(n2));
4481                   if (n2->text) eina_strbuf_free(n2->text);
4482                   free(n2);
4483                   n = eina_list_data_get(eina_list_last(format_hump));
4484                   if (n)
4485                     {
4486                        if (tcur.node == n)
4487                          {
4488                             if ((EINA_INLIST_GET(n))->next)
4489                               {
4490                                  tcur.node = (Evas_Object_Textblock_Node *) (EINA_INLIST_GET(n))->next;
4491                                  tcur.pos = 0;
4492                               }
4493                             else
4494                               {
4495                                  tcur.node = (Evas_Object_Textblock_Node *) (EINA_INLIST_GET(n))->next;
4496                                  if (tcur.node)
4497                                    {
4498                                       if (tcur.node->type == NODE_TEXT)
4499                                         tcur.pos = evas_common_font_utf8_get_last((unsigned char *)eina_strbuf_string_get(tcur.node->text), eina_strbuf_length_get(tcur.node->text));
4500                                       else
4501                                         tcur.pos = 0;
4502                                    }
4503                               }
4504                          }
4505                        o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_remove(EINA_INLIST_GET(o->nodes),
4506                                                                                    EINA_INLIST_GET(n));
4507                        if (n->text) eina_strbuf_free(n->text);
4508                        free(n);
4509                     }
4510                }
4511              else
4512                {
4513                   o->nodes = (Evas_Object_Textblock_Node *)eina_inlist_remove(EINA_INLIST_GET(o->nodes),
4514                                                                               EINA_INLIST_GET(n2));
4515                   if (n2->text) eina_strbuf_free(n2->text);
4516                   free(n2);
4517                }
4518           }
4519         if (format_hump) eina_list_free(format_hump);
4520         cur1->node = tcur.node;
4521         cur1->pos = tcur.pos;
4522         cur2->node = tcur.node;
4523         cur2->pos = tcur.pos;
4524      }
4525
4526    /* FIXME: adjust cursors that are affected by the change */
4527    /* this is temporary just avoiding segv's - it sets all other cursors to
4528     * the same pos as cur1 and cur2
4529     */
4530      {
4531         Eina_List *l;
4532         Evas_Textblock_Cursor *data;
4533
4534         if ((cur1 != o->cursor) && (cur2 != o->cursor))
4535           {
4536              evas_textblock_cursor_copy(cur1, o->cursor);
4537           }
4538         EINA_LIST_FOREACH(o->cursors, l, data)
4539           {
4540              if ((data != cur1) && (data != cur2))
4541                {
4542                   evas_textblock_cursor_copy(cur1, data);
4543                }
4544           }
4545      }
4546    if (cur1->node) _nodes_adjacent_merge(cur1->obj, cur1->node);
4547    if (cur2->node) _nodes_adjacent_merge(cur2->obj, cur2->node);
4548
4549    o->formatted.valid = 0;
4550    o->native.valid = 0;
4551    o->changed = 1;
4552    if (o->markup_text)
4553      {
4554         free(o->markup_text);
4555         o->markup_text = NULL;
4556      }
4557    evas_object_change(cur1->obj);
4558 }
4559
4560 /**
4561  * to be documented.
4562  * @param cur to be documented.
4563  * @return to be documented.
4564  */
4565 EAPI const char *
4566 evas_textblock_cursor_node_text_get(const Evas_Textblock_Cursor *cur)
4567 {
4568    if (!cur) return NULL;
4569    if (!cur->node) return NULL;
4570    if (cur->node->type == NODE_TEXT)
4571      {
4572         return eina_strbuf_string_get(cur->node->text);
4573      }
4574    return NULL;
4575 }
4576
4577 /**
4578  * to be documented.
4579  * @param cur to be documented.
4580  * @return to be documented.
4581  */
4582 EAPI int
4583 evas_textblock_cursor_node_text_length_get(const Evas_Textblock_Cursor *cur)
4584 {
4585    if (!cur) return 0;
4586    if (!cur->node) return 0;
4587    if (cur->node->type == NODE_TEXT)
4588      {
4589         return eina_strbuf_length_get(cur->node->text);
4590      }
4591    return 0;
4592 }
4593
4594 /**
4595  * to be documented.
4596  * @param cur to be documented.
4597  * @return to be documented.
4598  */
4599 EAPI const char *
4600 evas_textblock_cursor_node_format_get(const Evas_Textblock_Cursor *cur)
4601 {
4602    if (!cur) return NULL;
4603    if (!cur->node) return NULL;
4604    if (cur->node->type == NODE_FORMAT)
4605      {
4606         return eina_strbuf_string_get(cur->node->text);
4607      }
4608    return NULL;
4609 }
4610
4611 /**
4612  * to be documented.
4613  * @param cur to be documented.
4614  * @return to be documented.
4615  */
4616 EAPI Eina_Bool
4617 evas_textblock_cursor_node_format_is_visible_get(const Evas_Textblock_Cursor *cur)
4618 {
4619    Evas_Object_Textblock_Node *n;
4620
4621    if (!cur) return EINA_FALSE;
4622    n = cur->node;
4623    if (!n) return EINA_FALSE;
4624    if (n->type != NODE_FORMAT) return EINA_FALSE;
4625    if (!eina_strbuf_length_get(n->text)) return EINA_FALSE;
4626      {
4627         char *s;
4628         char *item;
4629         int visible = 0;
4630
4631         s = (char *)eina_strbuf_string_get(n->text);
4632         if (s[0] == '+' || s[0] == '-')
4633           {
4634              s++;
4635           }
4636         while ((item = _format_parse(&s)))
4637           {
4638              char tmp_delim = *s;
4639              *s = '\0';
4640              if ((!strcmp(item, "\n")) || (!strcmp(item, "\\n")))
4641                visible = 1;
4642              else if ((!strcmp(item, "\t")) || (!strcmp(item, "\\t")))
4643                visible = 1;
4644              *s = tmp_delim;
4645              if (visible) return EINA_TRUE;
4646           }
4647      }
4648    return EINA_FALSE;
4649 }
4650
4651 /**
4652  * to be documented.
4653  * @param cur1 to be documented.
4654  * @param cur2 to be documented.
4655  * @param format to be documented.
4656  * @return to be documented.
4657  */
4658 EAPI char *
4659 evas_textblock_cursor_range_text_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2, Evas_Textblock_Text_Type format)
4660 {
4661    Evas_Object_Textblock *o;
4662    Evas_Object_Textblock_Node *n1, *n2, *n;
4663    Eina_Strbuf *txt;
4664    char *s, *ret;
4665    int index;
4666
4667    if (!cur1) return NULL;
4668    if (!cur2) return NULL;
4669    if (cur1->obj != cur2->obj) return NULL;
4670    o = (Evas_Object_Textblock *)(cur1->obj->object_data);
4671    if (evas_textblock_cursor_compare(cur1, cur2) > 0)
4672      {
4673         const Evas_Textblock_Cursor *tc;
4674
4675         tc = cur1;
4676         cur1 = cur2;
4677         cur2 = tc;
4678      }
4679    n1 = cur1->node;
4680    n2 = cur2->node;
4681    index = cur2->pos;
4682    if ((!n1) || (!n2)) return NULL;
4683    if (n2->text)
4684      evas_common_font_utf8_get_next((unsigned char *)eina_strbuf_string_get(n2->text), &index);
4685    txt = eina_strbuf_new();
4686    EINA_INLIST_FOREACH(n1, n)
4687      {
4688         if ((n->type == NODE_TEXT) && (n->text))
4689           {
4690              s = (char *)eina_strbuf_string_get(n->text);
4691              if (format == EVAS_TEXTBLOCK_TEXT_MARKUP)
4692                {
4693                   const char *p, *ps, *pe;
4694
4695                   if (eina_strbuf_length_get(n->text))
4696                     {
4697                        if ((n == n1) && (n == n2))
4698                          {
4699                             ps = eina_strbuf_string_get(n->text) + cur1->pos;
4700                             pe = ps + index - cur1->pos;
4701                          }
4702                        else if (n == n1)
4703                          {
4704                             ps = eina_strbuf_string_get(n->text) + cur1->pos;
4705                             pe = ps + strlen(ps);
4706                          }
4707                        else if (n == n2)
4708                          {
4709                             ps = eina_strbuf_string_get(n->text);
4710                             pe = ps + cur2->pos + 1;
4711                          }
4712                        else
4713                          {
4714                             ps = eina_strbuf_string_get(n->text);
4715                             pe = ps + strlen(ps);
4716                          }
4717                        p = ps;
4718                        while (p < pe)
4719                          {
4720                             const char *escape;
4721                             int adv;
4722
4723                             if (!*p) break;
4724                             escape = _escaped_char_match(p, &adv);
4725                             if (escape)
4726                               {
4727                                  p += adv;
4728                                  eina_strbuf_append(txt, escape);
4729                               }
4730                             else
4731                               {
4732                                  eina_strbuf_append_char(txt, *p);
4733                                  p++;
4734                               }
4735                          }
4736                     }
4737                }
4738              else
4739                {
4740                   if ((n == n1) && (n == n2))
4741                     {
4742                        s += cur1->pos;
4743                        eina_strbuf_append_n(txt, s, index - cur1->pos);
4744                     }
4745                   else if (n == n1)
4746                     {
4747                        s += cur1->pos;
4748                        eina_strbuf_append(txt, s);
4749                     }
4750                   else if (n == n2)
4751                     {
4752                        eina_strbuf_append_n(txt, s, index);
4753                     }
4754                   else
4755                     {
4756                        eina_strbuf_append(txt, s);
4757                     }
4758                }
4759           }
4760         else if (n->text)
4761           {
4762              if (format == EVAS_TEXTBLOCK_TEXT_PLAIN)
4763                {
4764                   s = (char *)eina_strbuf_string_get(n->text);
4765                   while (*s)
4766                     {
4767                        if (*s == '\n')
4768                          eina_strbuf_append_char(txt, '\n');
4769                        else if (*s == '\t')
4770                          eina_strbuf_append_char(txt, '\t');
4771                        s++;
4772                     }
4773                }
4774              else if (format == EVAS_TEXTBLOCK_TEXT_MARKUP)
4775                {
4776                   size_t tag_len, replace_len = eina_strbuf_length_get(n->text);
4777                   const char *tag = _style_match_replace(o->style, eina_strbuf_string_get(n->text), replace_len, &tag_len);
4778                   eina_strbuf_append_char(txt, '<');
4779                   if (tag)
4780                     {
4781                        // FIXME: need to escape
4782                        eina_strbuf_append_length(txt, tag, tag_len);
4783                     }
4784                   else
4785                     {
4786                        int push = 0;
4787                        int pop = 0;
4788
4789                        // FIXME: need to escape
4790                        s = (char *)eina_strbuf_string_get(n->text);
4791                        if (*s == '+') push = 1;
4792                        if (*s == '-') pop = 1;
4793                        while ((*s == ' ') || (*s == '+') || (*s == '-')) s++;
4794                        if (pop) eina_strbuf_append_char(txt, '/');
4795                        if (push) eina_strbuf_append(txt, "+ ");
4796                        eina_strbuf_append(txt, s);
4797                     }
4798                   eina_strbuf_append_char(txt, '>');
4799                }
4800           }
4801         if (n == n2) break;
4802      }
4803    ret = eina_strbuf_string_steal(txt);
4804    eina_strbuf_free(txt);
4805    return ret;
4806 }
4807
4808 /**
4809  * to be documented.
4810  * @param cur to be documented.
4811  * @param cx to be documented.
4812  * @param cy to be documented.
4813  * @param cw to be documented.
4814  * @param ch to be documented.
4815  * @return to be documented.
4816  */
4817 EAPI int
4818 evas_textblock_cursor_char_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
4819 {
4820    Evas_Object_Textblock *o;
4821    Evas_Object_Textblock_Line *ln = NULL;
4822    Evas_Object_Textblock_Item *it = NULL;
4823    Evas_Object_Textblock_Format_Item *fi = NULL;
4824    int x = 0, y = 0, w = 0, h = 0;
4825    int pos, ret;
4826
4827    if (!cur) return -1;
4828    o = (Evas_Object_Textblock *)(cur->obj->object_data);
4829    if (!cur->node)
4830      {
4831         if (!o->nodes)
4832           {
4833              ln = o->lines;
4834              if (!ln) return -1;
4835              if (cx) *cx = ln->x;
4836              if (cy) *cy = ln->y;
4837              if (cw) *cw = ln->w;
4838              if (ch) *ch = ln->h;
4839              return ln->line_no;
4840           }
4841         else
4842           return -1;
4843      }
4844    if (!o->formatted.valid) _relayout(cur->obj);
4845    if (cur->node->type == NODE_FORMAT)
4846      {
4847         _find_layout_format_item_line_match(cur->obj, cur->node, &ln, &fi);
4848      }
4849    else
4850      {
4851         _find_layout_item_line_match(cur->obj, cur->node, cur->pos, cur->eol, &ln, &it);
4852      }
4853    if (!ln)
4854      {
4855         return -1;
4856      }
4857    if (it)
4858      {
4859         pos = cur->pos - it->source_pos;
4860         ret = -1;
4861         if (cur->eol)
4862           {
4863              int pos2;
4864
4865              pos2 = pos;
4866              evas_common_font_utf8_get_next((unsigned char *)(it->text), &pos2);
4867              if (pos2 > pos) pos = pos2;
4868           }
4869         if (pos < 0) pos = 0;
4870         if (it->format->font.font)
4871           ret = cur->ENFN->font_char_coords_get(cur->ENDT, it->format->font.font,
4872                                                 it->text,
4873                                                 pos,
4874                                                 &x, &y, &w, &h);
4875         if (ret <= 0)
4876           {
4877              if (it->format->font.font)
4878                 cur->ENFN->font_string_size_get(cur->ENDT, it->format->font.font,
4879                                                 it->text, &w, &h);
4880              x = w;
4881              y = 0;
4882              w = 0;
4883           }
4884         x = ln->x + it->x - it->inset + x;
4885         if (x < ln->x)
4886           {
4887              x = ln->x;
4888              w -= (ln->x - x);
4889           }
4890         y = ln->y;
4891         h = ln->h;
4892      }
4893    else if (fi)
4894      {
4895         x = ln->x + fi->x;
4896         y = ln->y;
4897         w = fi->w;
4898         h = ln->h;
4899      }
4900    else
4901      {
4902         return -1;
4903      }
4904    if (cx) *cx = x;
4905    if (cy) *cy = y;
4906    if (cw) *cw = w;
4907    if (ch) *ch = h;
4908    return ln->line_no;
4909 }
4910
4911 /**
4912  * to be documented.
4913  * @param cur to be documented.
4914  * @param cx to be documented.
4915  * @param cy to be documented.
4916  * @param cw to be documented.
4917  * @param ch to be documented.
4918  * @return to be documented.
4919  */
4920 EAPI int
4921 evas_textblock_cursor_line_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
4922 {
4923    Evas_Object_Textblock *o;
4924    Evas_Object_Textblock_Line *ln = NULL;
4925    Evas_Object_Textblock_Item *it = NULL;
4926    Evas_Object_Textblock_Format_Item *fi = NULL;
4927    int x, y, w, h;
4928
4929    if (!cur) return -1;
4930    o = (Evas_Object_Textblock *)(cur->obj->object_data);
4931    if (!o->formatted.valid) _relayout(cur->obj);
4932    if (!cur->node)
4933      {
4934         ln = o->lines;
4935      }
4936    else
4937      {
4938         if (cur->node->type == NODE_FORMAT)
4939           _find_layout_format_item_line_match(cur->obj, cur->node, &ln, &fi);
4940         else
4941           _find_layout_item_line_match(cur->obj, cur->node, cur->pos, cur->eol, &ln, &it);
4942      }
4943    if (!ln) return -1;
4944    x = ln->x;
4945    y = ln->y;
4946    w = ln->w;
4947    h = ln->h;
4948    if (cx) *cx = x;
4949    if (cy) *cy = y;
4950    if (cw) *cw = w;
4951    if (ch) *ch = h;
4952    return ln->line_no;
4953 }
4954
4955 /**
4956  * to be documented.
4957  * @param cur to be documented.
4958  * @param x to be documented.
4959  * @param y to be documented.
4960  * @return to be documented.
4961  */
4962 EAPI Eina_Bool
4963 evas_textblock_cursor_char_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord x, Evas_Coord y)
4964 {
4965    Evas_Object_Textblock *o;
4966    Evas_Object_Textblock_Line *ln;
4967    Evas_Object_Textblock_Item *it = NULL, *it_break = NULL;
4968    Evas_Object_Textblock_Format_Item *fi = NULL;
4969
4970    if (!cur) return EINA_FALSE;
4971    o = (Evas_Object_Textblock *)(cur->obj->object_data);
4972    if (!o->formatted.valid) _relayout(cur->obj);
4973    x += o->style_pad.l;
4974    y += o->style_pad.t;
4975    EINA_INLIST_FOREACH(o->lines, ln)
4976      {
4977         if (ln->y > y) break;
4978         if ((ln->y <= y) && ((ln->y + ln->h) > y))
4979           {
4980              EINA_INLIST_FOREACH(ln->items, it)
4981                {
4982                   if ((it->x + ln->x) > x)
4983                     {
4984                        it_break = it;
4985                        break;
4986                     }
4987                   if (((it->x + ln->x) <= x) && (((it->x + ln->x) + it->w) > x))
4988                     {
4989                        int pos;
4990                        int cx, cy, cw, ch;
4991
4992                        pos = -1;
4993                        if (it->format->font.font)
4994                          pos = cur->ENFN->font_char_at_coords_get(cur->ENDT,
4995                                                                   it->format->font.font,
4996                                                                   it->text,
4997                                                                   x - it->x - ln->x, 0,
4998                                                                   &cx, &cy, &cw, &ch);
4999                        if (pos < 0)
5000                          return EINA_FALSE;
5001                        cur->pos = pos + it->source_pos;
5002                        cur->node = it->source_node;
5003                        return 1;
5004                     }
5005                }
5006              EINA_INLIST_FOREACH(ln->format_items, fi)
5007                {
5008                   if ((fi->x + ln->x) > x) break;
5009                   if (((fi->x + ln->x) <= x) && (((fi->x + ln->x) + fi->w) > x))
5010                     {
5011                        cur->pos = 0;
5012                        cur->eol = 0;
5013                        cur->node = fi->source_node;
5014                        return EINA_TRUE;
5015                     }
5016                }
5017              if (it_break)
5018                {
5019                   it = it_break;
5020                   cur->pos = it->source_pos;
5021                   cur->eol = 0;
5022                   cur->node = it->source_node;
5023                   return EINA_TRUE;
5024                }
5025           }
5026      }
5027    return EINA_FALSE;
5028 }
5029
5030 /**
5031  * to be documented.
5032  * @param cur to be documented.
5033  * @param y to be documented.
5034  * @return to be documented.
5035  */
5036 EAPI int
5037 evas_textblock_cursor_line_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord y)
5038 {
5039    Evas_Object_Textblock *o;
5040    Evas_Object_Textblock_Line *ln;
5041
5042    if (!cur) return -1;
5043    o = (Evas_Object_Textblock *)(cur->obj->object_data);
5044    if (!o->formatted.valid) _relayout(cur->obj);
5045    y += o->style_pad.t;
5046    EINA_INLIST_FOREACH(o->lines, ln)
5047      {
5048         if (ln->y > y) break;
5049         if ((ln->y <= y) && ((ln->y + ln->h) > y))
5050           {
5051              evas_textblock_cursor_line_set(cur, ln->line_no);
5052              return ln->line_no;
5053           }
5054      }
5055    return -1;
5056 }
5057
5058 /**
5059  * to be documented.
5060  * @param cur1 to be documented.
5061  * @param cur2 to be documented.
5062  * @return to be documented.
5063  */
5064 EAPI Eina_List *
5065 evas_textblock_cursor_range_geometry_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
5066 {
5067    Eina_List *rects = NULL;
5068    Evas_Coord cx, cy, cw, ch, lx, ly, lw, lh;
5069    Evas_Textblock_Rectangle *tr;
5070    int i, line, line2;
5071
5072    if (!cur1) return NULL;
5073    if (!cur2) return NULL;
5074    if (cur1->obj != cur2->obj) return NULL;
5075    if (evas_textblock_cursor_compare(cur1, cur2) > 0)
5076      {
5077         const Evas_Textblock_Cursor *tc;
5078
5079         tc = cur1;
5080         cur1 = cur2;
5081         cur2 = tc;
5082      }
5083    line = evas_textblock_cursor_char_geometry_get(cur1, &cx, &cy, &cw, &ch);
5084    if (line < 0) return NULL;
5085    line = evas_textblock_cursor_line_geometry_get(cur1, &lx, &ly, &lw, &lh);
5086    if (line < 0) return NULL;
5087    line2 = evas_textblock_cursor_line_geometry_get(cur2, NULL, NULL, NULL, NULL);
5088    if (line2 < 0) return NULL;
5089    if (line == line2)
5090      {
5091         tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
5092         rects = eina_list_append(rects, tr);
5093         tr->x = cx;
5094         tr->y = ly;
5095         tr->h = lh;
5096         line = evas_textblock_cursor_char_geometry_get(cur2, &cx, &cy, &cw, &ch);
5097         if (line < 0)
5098           {
5099              while (rects)
5100                {
5101                   free(rects->data);
5102                   rects = eina_list_remove_list(rects, rects);
5103                }
5104              return NULL;
5105           }
5106         tr->w = cx + cw - tr->x;
5107      }
5108    else
5109      {
5110         tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
5111         rects = eina_list_append(rects, tr);
5112         tr->x = cx;
5113         tr->y = ly;
5114         tr->h = lh;
5115         tr->w = lx + lw - cx;
5116         for (i = line +1; i < line2; i++)
5117           {
5118              evas_object_textblock_line_number_geometry_get(cur1->obj, i, &lx, &ly, &lw, &lh);
5119              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
5120              rects = eina_list_append(rects, tr);
5121              tr->x = lx;
5122              tr->y = ly;
5123              tr->h = lh;
5124              tr->w = lw;
5125           }
5126         line = evas_textblock_cursor_char_geometry_get(cur2, &cx, &cy, &cw, &ch);
5127         if (line < 0)
5128           {
5129              while (rects)
5130                {
5131                   free(rects->data);
5132                   rects = eina_list_remove_list(rects, rects);
5133                }
5134              return NULL;
5135           }
5136         line = evas_textblock_cursor_line_geometry_get(cur2, &lx, &ly, &lw, &lh);
5137         if (line < 0)
5138           {
5139              while (rects)
5140                {
5141                   free(rects->data);
5142                   rects = eina_list_remove_list(rects, rects);
5143                }
5144              return NULL;
5145           }
5146         tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
5147         rects = eina_list_append(rects, tr);
5148         tr->x = lx;
5149         tr->y = ly;
5150         tr->h = lh;
5151         tr->w = cx + cw - lx;
5152      }
5153    return rects;
5154 }
5155
5156 /**
5157  * to be documented.
5158  * @param cur to be documented.
5159  * @param cx to be documented.
5160  * @param cy to be documented.
5161  * @param cw to be documented.
5162  * @param ch to be documented.
5163  * @return to be documented.
5164  */
5165 EAPI Eina_Bool
5166 evas_textblock_cursor_format_item_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
5167 {
5168    Evas_Object_Textblock *o;
5169    Evas_Object_Textblock_Line *ln = NULL;
5170    Evas_Object_Textblock_Format_Item *fi = NULL;
5171    Evas_Coord x, y, w, h;
5172
5173    if (!cur) return 0;
5174    o = (Evas_Object_Textblock *)(cur->obj->object_data);
5175    if (!o->formatted.valid) _relayout(cur->obj);
5176    _find_layout_format_item_line_match(cur->obj, cur->node, &ln, &fi);
5177    if ((!ln) || (!fi)) return 0;
5178    x = ln->x + fi->x;
5179    y = ln->y + ln->baseline + fi->y;
5180    w = fi->w;
5181    h = fi->h;
5182    if (cx) *cx = x;
5183    if (cy) *cy = y;
5184    if (cw) *cw = w;
5185    if (ch) *ch = h;
5186    return 1;
5187 }
5188
5189 /**
5190  * To be documented.
5191  *
5192  * FIXME: To be fixed.
5193  *
5194  */
5195 EAPI Eina_Bool
5196 evas_textblock_cursor_eol_get(const Evas_Textblock_Cursor *cur)
5197 {
5198    if (!cur) return EINA_FALSE;
5199    return cur->eol;
5200 }
5201
5202 /**
5203  * To be documented.
5204  *
5205  * FIXME: To be fixed.
5206  *
5207  */
5208 EAPI void
5209 evas_textblock_cursor_eol_set(Evas_Textblock_Cursor *cur, Eina_Bool eol)
5210 {
5211    if (!cur) return;
5212    cur->eol = eol;
5213 }
5214
5215
5216 /* general controls */
5217 /**
5218  * to be documented.
5219  * @param obj to be documented.
5220  * @param line to be documented.
5221  * @param cx to be documented.
5222  * @param cy to be documented.
5223  * @param cw to be documented.
5224  * @param ch to be documented.
5225  * @return to be documented.
5226  */
5227 EAPI Eina_Bool
5228 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)
5229 {
5230    Evas_Object_Textblock_Line *ln;
5231
5232    TB_HEAD_RETURN(0);
5233    ln = _find_layout_line_num(obj, line);
5234    if (!ln) return EINA_FALSE;
5235    if (cx) *cx = ln->x;
5236    if (cy) *cy = ln->y;
5237    if (cw) *cw = ln->w;
5238    if (ch) *ch = ln->h;
5239    return EINA_TRUE;
5240 }
5241
5242 /**
5243  * to be documented.
5244  * @param obj to be documented.
5245  * @return Returns no value.
5246  */
5247 EAPI void
5248 evas_object_textblock_clear(Evas_Object *obj)
5249 {
5250    Eina_List *l;
5251    Evas_Textblock_Cursor *cur;
5252
5253    TB_HEAD();
5254    _nodes_clear(obj);
5255    o->cursor->node = NULL;
5256    o->cursor->pos = 0;
5257    o->cursor->eol = 0;
5258    EINA_LIST_FOREACH(o->cursors, l, cur)
5259      {
5260         cur->node = NULL;
5261         cur->pos = 0;
5262         cur->eol = 0;
5263      }
5264    if (o->lines)
5265      {
5266         _lines_clear(obj, o->lines);
5267         o->lines = NULL;
5268      }
5269    o->formatted.valid = 0;
5270    o->native.valid = 0;
5271    o->changed = 1;
5272    if (o->markup_text)
5273      {
5274         free(o->markup_text);
5275         o->markup_text = NULL;
5276      }
5277    evas_object_change(obj);
5278    /* FIXME: adjust cursors that are affected by the change */
5279 }
5280
5281 /**
5282  * to be documented.
5283  * @param obj to be documented.
5284  * @param w to be documented.
5285  * @param h to be documented.
5286  * @return Returns no value.
5287  */
5288 EAPI void
5289 evas_object_textblock_size_formatted_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
5290 {
5291    TB_HEAD();
5292    if (!o->formatted.valid) _relayout(obj);
5293    if (w) *w = o->formatted.w;
5294    if (h) *h = o->formatted.h;
5295 }
5296
5297 /**
5298  * to be documented.
5299  * @param obj to be documented.
5300  * @param w to be documented.
5301  * @param h to be documented.
5302  * @return Returns no value.
5303  */
5304 EAPI void
5305 evas_object_textblock_size_native_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
5306 {
5307    TB_HEAD();
5308    if (!o->native.valid)
5309      {
5310         _layout(obj,
5311                 1,
5312                 -1, -1,
5313                 &o->native.w, &o->native.h);
5314         o->native.valid = 1;
5315      }
5316    if (w) *w = o->native.w;
5317    if (h) *h = o->native.h;
5318 }
5319
5320 /**
5321  * to be documented.
5322  * @param obj to be documented.
5323  * @param l to be documented.
5324  * @param r to be documented.
5325  * @param t to be documented.
5326  * @param b to be documented.
5327  * @return Returns no value.
5328  */
5329 EAPI void
5330 evas_object_textblock_style_insets_get(const Evas_Object *obj, Evas_Coord *l, Evas_Coord *r, Evas_Coord *t, Evas_Coord *b)
5331 {
5332    TB_HEAD();
5333    if (!o->formatted.valid) _relayout(obj);
5334    if (l) *l = o->style_pad.l;
5335    if (r) *r = o->style_pad.r;
5336    if (t) *t = o->style_pad.t;
5337    if (b) *b = o->style_pad.b;
5338 }
5339
5340 /**
5341  * @}
5342  */
5343
5344 /* all nice and private */
5345 static void
5346 evas_object_textblock_init(Evas_Object *obj)
5347 {
5348    Evas_Object_Textblock *o;
5349
5350    /* alloc image ob, setup methods and default values */
5351    obj->object_data = evas_object_textblock_new();
5352    /* set up default settings for this kind of object */
5353    obj->cur.color.r = 255;
5354    obj->cur.color.g = 255;
5355    obj->cur.color.b = 255;
5356    obj->cur.color.a = 255;
5357    obj->cur.geometry.x = 0.0;
5358    obj->cur.geometry.y = 0.0;
5359    obj->cur.geometry.w = 0.0;
5360    obj->cur.geometry.h = 0.0;
5361    obj->cur.layer = 0;
5362    /* set up object-specific settings */
5363    obj->prev = obj->cur;
5364    /* set up methods (compulsory) */
5365    obj->func = &object_func;
5366    obj->type = o_type;
5367
5368    o = (Evas_Object_Textblock *)(obj->object_data);
5369    o->cursor->obj = obj;
5370 }
5371
5372 static void *
5373 evas_object_textblock_new(void)
5374 {
5375    Evas_Object_Textblock *o;
5376
5377    /* alloc obj private data */
5378    o = calloc(1, sizeof(Evas_Object_Textblock));
5379    o->magic = MAGIC_OBJ_TEXTBLOCK;
5380    o->cursor = calloc(1, sizeof(Evas_Textblock_Cursor));
5381    return o;
5382 }
5383
5384 static void
5385 evas_object_textblock_free(Evas_Object *obj)
5386 {
5387    Evas_Object_Textblock *o;
5388
5389    evas_object_textblock_clear(obj);
5390    evas_object_textblock_style_set(obj, NULL);
5391    o = (Evas_Object_Textblock *)(obj->object_data);
5392    free(o->cursor);
5393    while (o->cursors)
5394      {
5395         Evas_Textblock_Cursor *cur;
5396
5397         cur = (Evas_Textblock_Cursor *)o->cursors->data;
5398         o->cursors = eina_list_remove_list(o->cursors, o->cursors);
5399         free(cur);
5400      }
5401    if (o->repch) eina_stringshare_del(o->repch);
5402    o->magic = 0;
5403    free(o);
5404 }
5405
5406 static void
5407 evas_object_textblock_render(Evas_Object *obj, void *output, void *context, void *surface, int x, int y)
5408 {
5409    Evas_Object_Textblock_Line *ln;
5410    Evas_Object_Textblock *o;
5411    int i, j;
5412    int pback = 0, backx = 0;
5413    int pline = 0, linex = 0;
5414    int pline2 = 0, line2x = 0;
5415    int pstrike = 0, strikex = 0;
5416    int x2;
5417    unsigned char r = 0, g = 0, b = 0, a = 0;
5418    unsigned char r2 = 0, g2 = 0, b2 = 0, a2 = 0;
5419    unsigned char r3 = 0, g3 = 0, b3 = 0, a3 = 0;
5420    int cx, cy, cw, ch, clip;
5421    const char vals[5][5] =
5422      {
5423           {0, 1, 2, 1, 0},
5424           {1, 3, 4, 3, 1},
5425           {2, 4, 5, 4, 2},
5426           {1, 3, 4, 3, 1},
5427           {0, 1, 2, 1, 0}
5428      };
5429
5430    /* render object to surface with context, and offxet by x,y */
5431    o = (Evas_Object_Textblock *)(obj->object_data);
5432    obj->layer->evas->engine.func->context_multiplier_unset(output,
5433                                                            context);
5434    clip = ENFN->context_clip_get(output, context, &cx, &cy, &cw, &ch);
5435 #define ITEM_WALK() \
5436    EINA_INLIST_FOREACH(o->lines, ln) \
5437      { \
5438         Evas_Object_Textblock_Item *it; \
5439         \
5440         pback = 0; \
5441         pline = 0; \
5442         pline2 = 0; \
5443         pstrike = 0; \
5444         if (clip) \
5445           { \
5446              if ((obj->cur.geometry.y + y + ln->y + ln->h) < (cy - 20)) \
5447                continue; \
5448              if ((obj->cur.geometry.y + y + ln->y) > (cy + ch + 20)) \
5449                break; \
5450           } \
5451         EINA_INLIST_FOREACH(ln->items, it) \
5452           { \
5453              int yoff; \
5454              \
5455              yoff = ln->baseline; \
5456              if (it->format->valign != -1.0) \
5457                yoff = (it->format->valign * (double)(ln->h - it->h)) + it->baseline; \
5458              if (clip) \
5459                { \
5460                   if ((obj->cur.geometry.x + x + ln->x + it->x - it->inset + it->w) < (cx - 20)) \
5461                     continue; \
5462                   if ((obj->cur.geometry.x + x + ln->x + it->x - it->inset) > (cx + cw + 20)) \
5463                     break; \
5464                }
5465              
5466 #define ITEM_WALK_END() \
5467           } \
5468      }
5469 #define COLOR_SET(col) \
5470         ENFN->context_color_set(output, context, \
5471                                 (obj->cur.cache.clip.r * it->format->color.col.r) / 255, \
5472                                 (obj->cur.cache.clip.g * it->format->color.col.g) / 255, \
5473                                 (obj->cur.cache.clip.b * it->format->color.col.b) / 255, \
5474                                 (obj->cur.cache.clip.a * it->format->color.col.a) / 255);
5475 #define COLOR_SET_AMUL(col, amul) \
5476         ENFN->context_color_set(output, context, \
5477                                 (obj->cur.cache.clip.r * it->format->color.col.r * (amul)) / 65025, \
5478                                 (obj->cur.cache.clip.g * it->format->color.col.g * (amul)) / 65025, \
5479                                 (obj->cur.cache.clip.b * it->format->color.col.b * (amul)) / 65025, \
5480                                 (obj->cur.cache.clip.a * it->format->color.col.a * (amul)) / 65025);
5481 #define DRAW_TEXT(ox, oy) \
5482    if (it->format->font.font) ENFN->font_draw(output, context, surface, it->format->font.font, \
5483                                                  obj->cur.geometry.x + ln->x + it->x - it->inset + x + (ox), \
5484                                                  obj->cur.geometry.y + ln->y + yoff + y + (oy), \
5485                                                  it->w, it->h, it->w, it->h, it->text);
5486 #if 0
5487 //#define DRAW_TEXT(ox, oy) \
5488 //   if (it->format->font.font) ENFN->font_draw(output, context, surface, it->format->font.font, \
5489 //                                               obj->cur.geometry.x + ln->x + it->x - it->inset + x + (ox), \
5490 //                                               obj->cur.geometry.y + ln->y + yoff + y + (oy), \
5491 //                                               obj->cur.cache.geometry.x + ln->x + it->x - it->inset + x + (ox), \
5492 //                                               obj->cur.cache.geometry.y + ln->y + yoff + y + (oy), \
5493 //                                               it->w, it->h, it->w, it->h, it->text);
5494 #endif
5495 #define ITEM_WALK_LINE_SKIP_DROP() \
5496    if ((ln->y + ln->h) <= 0) continue; \
5497    if (ln->y > obj->cur.geometry.h) break
5498
5499    pback = 0;
5500    /* backing */
5501    ITEM_WALK();
5502    ITEM_WALK_LINE_SKIP_DROP();
5503    if ((it->format->backing) && (!pback) && ((EINA_INLIST_GET(it))->next))
5504      {
5505         pback = 1;
5506         backx = it->x;
5507         r = it->format->color.backing.r;
5508         g = it->format->color.backing.g;
5509         b = it->format->color.backing.b;
5510         a = it->format->color.backing.a;
5511      }
5512    else if (((pback) && (!it->format->backing)) ||
5513             (!(EINA_INLIST_GET(it))->next) ||
5514             (it->format->color.backing.r != r) ||
5515             (it->format->color.backing.g != g) ||
5516             (it->format->color.backing.b != b) ||
5517             (it->format->color.backing.a != a))
5518      {
5519         if ((it->format->backing) && (!pback) && (!(EINA_INLIST_GET(it))->next))
5520           {
5521              r = it->format->color.backing.r;
5522              g = it->format->color.backing.g;
5523              b = it->format->color.backing.b;
5524              a = it->format->color.backing.a;
5525              pback = 1;
5526              backx = it->x;
5527           }
5528         if (!it->format->backing) x2 = it->x;
5529         else x2 = it->x + it->w;
5530         if ((pback) && (x2 > backx))
5531           {
5532              ENFN->context_color_set(output,
5533                                      context,
5534                                      (obj->cur.cache.clip.r * r) / 255,
5535                                      (obj->cur.cache.clip.g * g) / 255,
5536                                      (obj->cur.cache.clip.b * b) / 255,
5537                                      (obj->cur.cache.clip.a * a) / 255);
5538              ENFN->rectangle_draw(output,
5539                                   context,
5540                                   surface,
5541                                   obj->cur.geometry.x + ln->x + backx + x,
5542                                   obj->cur.geometry.y + ln->y + y,
5543 ////                              obj->cur.cache.geometry.x + ln->x + backx + x,
5544 ////                              obj->cur.cache.geometry.y + ln->y + y,
5545                                   x2 - backx,
5546                                   ln->h);
5547           }
5548         pback = it->format->backing;
5549         backx = it->x;
5550         r = it->format->color.backing.r;
5551         g = it->format->color.backing.g;
5552         b = it->format->color.backing.b;
5553         a = it->format->color.backing.a;
5554      }
5555    ITEM_WALK_END();
5556
5557    /* shadows */
5558    ITEM_WALK();
5559    ITEM_WALK_LINE_SKIP_DROP();
5560    if (it->format->style == EVAS_TEXT_STYLE_SHADOW)
5561      {
5562         COLOR_SET(shadow);
5563         DRAW_TEXT(1, 1);
5564      }
5565    else if ((it->format->style == EVAS_TEXT_STYLE_OUTLINE_SHADOW) ||
5566             (it->format->style == EVAS_TEXT_STYLE_FAR_SHADOW))
5567      {
5568         COLOR_SET(shadow);
5569         DRAW_TEXT(2, 2);
5570      }
5571    else if ((it->format->style == EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW) ||
5572             (it->format->style == EVAS_TEXT_STYLE_FAR_SOFT_SHADOW))
5573      {
5574         for (j = 0; j < 5; j++)
5575           {
5576              for (i = 0; i < 5; i++)
5577                {
5578                   if (vals[i][j] != 0)
5579                     {
5580                        COLOR_SET_AMUL(shadow, vals[i][j] * 50);
5581                        DRAW_TEXT(i, j);
5582                     }
5583                }
5584           }
5585      }
5586    else if (it->format->style == EVAS_TEXT_STYLE_SOFT_SHADOW)
5587      {
5588         for (j = 0; j < 5; j++)
5589           {
5590              for (i = 0; i < 5; i++)
5591                {
5592                   if (vals[i][j] != 0)
5593                     {
5594                        COLOR_SET_AMUL(shadow, vals[i][j] * 50);
5595                        DRAW_TEXT(i - 1, j - 1);
5596                     }
5597                }
5598           }
5599      }
5600    ITEM_WALK_END();
5601
5602    /* glows */
5603    ITEM_WALK();
5604    ITEM_WALK_LINE_SKIP_DROP();
5605    if (it->format->style == EVAS_TEXT_STYLE_GLOW)
5606      {
5607         for (j = 0; j < 5; j++)
5608           {
5609              for (i = 0; i < 5; i++)
5610                {
5611                   if (vals[i][j] != 0)
5612                     {
5613                        COLOR_SET_AMUL(glow, vals[i][j] * 50);
5614                        DRAW_TEXT(i - 2, j - 2);
5615                     }
5616                }
5617           }
5618         COLOR_SET(glow2);
5619         DRAW_TEXT(-1, 0);
5620         DRAW_TEXT(1, 0);
5621         DRAW_TEXT(0, -1);
5622         DRAW_TEXT(0, 1);
5623      }
5624    ITEM_WALK_END();
5625
5626    /* outlines */
5627    ITEM_WALK();
5628    ITEM_WALK_LINE_SKIP_DROP();
5629    if ((it->format->style == EVAS_TEXT_STYLE_OUTLINE) ||
5630        (it->format->style == EVAS_TEXT_STYLE_OUTLINE_SHADOW) ||
5631        (it->format->style == EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW))
5632      {
5633         COLOR_SET(outline);
5634         DRAW_TEXT(-1, 0);
5635         DRAW_TEXT(1, 0);
5636         DRAW_TEXT(0, -1);
5637         DRAW_TEXT(0, 1);
5638      }
5639    else if (it->format->style == EVAS_TEXT_STYLE_SOFT_OUTLINE)
5640      {
5641         for (j = 0; j < 5; j++)
5642           {
5643              for (i = 0; i < 5; i++)
5644                {
5645                   if (((i != 2) || (j != 2)) && (vals[i][j] != 0))
5646                     {
5647                        COLOR_SET_AMUL(outline, vals[i][j] * 50);
5648                        DRAW_TEXT(i - 2, j - 2);
5649                     }
5650                }
5651           }
5652      }
5653    ITEM_WALK_END();
5654
5655    /* normal text */
5656    ITEM_WALK();
5657    ITEM_WALK_LINE_SKIP_DROP();
5658    COLOR_SET(normal);
5659    DRAW_TEXT(0, 0);
5660    if ((it->format->strikethrough) && (!pstrike) && ((EINA_INLIST_GET(it))->next))
5661      {
5662         pstrike = 1;
5663         strikex = it->x;
5664         r3 = it->format->color.strikethrough.r;
5665         g3 = it->format->color.strikethrough.g;
5666         b3 = it->format->color.strikethrough.b;
5667         a3 = it->format->color.strikethrough.a;
5668      }
5669    else if (((pstrike) && (!it->format->strikethrough)) ||
5670             (!(EINA_INLIST_GET(it))->next) ||
5671             (it->format->color.strikethrough.r != r3) ||
5672             (it->format->color.strikethrough.g != g3) ||
5673             (it->format->color.strikethrough.b != b3) ||
5674             (it->format->color.strikethrough.a != a3))
5675      {
5676         if ((it->format->strikethrough) && (!pstrike))
5677           {
5678              strikex = it->x;
5679              r3 = it->format->color.strikethrough.r;
5680              g3 = it->format->color.strikethrough.g;
5681              b3 = it->format->color.strikethrough.b;
5682              a3 = it->format->color.strikethrough.a;
5683           }
5684         x2 = it->x + it->w;
5685         if (!it->format->strikethrough)
5686           {
5687              x2 = it->x;
5688              pstrike = 0;
5689           }
5690         if (x2 > strikex)
5691           {
5692              ENFN->context_color_set(output,
5693                                      context,
5694                                      (obj->cur.cache.clip.r * r3) / 255,
5695                                      (obj->cur.cache.clip.g * g3) / 255,
5696                                      (obj->cur.cache.clip.b * b3) / 255,
5697                                      (obj->cur.cache.clip.a * a3) / 255);
5698              ENFN->rectangle_draw(output,
5699                                   context,
5700                                   surface,
5701                                   obj->cur.geometry.x + ln->x + strikex + x,
5702                                   obj->cur.geometry.y + ln->y + y + (ln->h / 2),
5703 ////                              obj->cur.cache.geometry.x + ln->x + strikex + x,
5704 ////                              obj->cur.cache.geometry.y + ln->y + y + (ln->h / 2),
5705                                   x2 - strikex,
5706                                   1);
5707           }
5708         if (it->format->strikethrough) pstrike = 1;
5709         strikex = it->x;
5710         r3 = it->format->color.strikethrough.r;
5711         g3 = it->format->color.strikethrough.g;
5712         b3 = it->format->color.strikethrough.b;
5713         a3 = it->format->color.strikethrough.a;
5714      }
5715    if ((it->format->underline) && (!pline) && ((EINA_INLIST_GET(it))->next))
5716      {
5717         pline = 1;
5718         linex = it->x;
5719         r = it->format->color.underline.r;
5720         g = it->format->color.underline.g;
5721         b = it->format->color.underline.b;
5722         a = it->format->color.underline.a;
5723      }
5724    else if (((pline) && (!it->format->underline)) ||
5725             (!(EINA_INLIST_GET(it))->next) ||
5726             (it->format->color.underline.r != r) ||
5727             (it->format->color.underline.g != g) ||
5728             (it->format->color.underline.b != b) ||
5729             (it->format->color.underline.a != a))
5730      {
5731         if ((it->format->underline) && (!pline))
5732           {
5733              linex = it->x;
5734              r = it->format->color.underline.r;
5735              g = it->format->color.underline.g;
5736              b = it->format->color.underline.b;
5737              a = it->format->color.underline.a;
5738           }
5739         x2 = it->x + it->w;
5740         if (!it->format->underline)
5741           {
5742              x2 = it->x;
5743              pline = 0;
5744           }
5745         if (x2 > linex)
5746           {
5747              ENFN->context_color_set(output,
5748                                      context,
5749                                      (obj->cur.cache.clip.r * r) / 255,
5750                                      (obj->cur.cache.clip.g * g) / 255,
5751                                      (obj->cur.cache.clip.b * b) / 255,
5752                                      (obj->cur.cache.clip.a * a) / 255);
5753              ENFN->rectangle_draw(output,
5754                                   context,
5755                                   surface,
5756                                   obj->cur.geometry.x + ln->x + linex + x,
5757                                   obj->cur.geometry.y + ln->y + y + ln->baseline + 1,
5758 ////                              obj->cur.cache.geometry.x + ln->x + linex + x,
5759 ////                              obj->cur.cache.geometry.y + ln->y + y + ln->baseline + 1,
5760                                   x2 - linex,
5761                                   1);
5762           }
5763         if (it->format->underline) pline = 1;
5764         linex = it->x;
5765         r = it->format->color.underline.r;
5766         g = it->format->color.underline.g;
5767         b = it->format->color.underline.b;
5768         a = it->format->color.underline.a;
5769      }
5770    if ((it->format->underline2) && (!pline2) && ((EINA_INLIST_GET(it))->next))
5771      {
5772         pline2 = 1;
5773         line2x = it->x;
5774         r2 = it->format->color.underline2.r;
5775         g2 = it->format->color.underline2.g;
5776         b2 = it->format->color.underline2.b;
5777         a2 = it->format->color.underline2.a;
5778      }
5779    else if (((pline2) && (!it->format->underline2)) ||
5780             (!(EINA_INLIST_GET(it))->next) ||
5781             (it->format->color.underline2.r != r2) ||
5782             (it->format->color.underline2.g != g2) ||
5783             (it->format->color.underline2.b != b2) ||
5784             (it->format->color.underline2.a != a2))
5785      {
5786         if ((it->format->underline2) && (!pline2))
5787           {
5788              line2x = it->x;
5789              r2 = it->format->color.underline2.r;
5790              g2 = it->format->color.underline2.g;
5791              b2 = it->format->color.underline2.b;
5792              a2 = it->format->color.underline2.a;
5793           }
5794         x2 = it->x + it->w;
5795         if (!it->format->underline2)
5796           {
5797              x2 = it->x;
5798              pline2 = 0;
5799           }
5800         if (x2 > line2x)
5801           {
5802              ENFN->context_color_set(output,
5803                                      context,
5804                                      (obj->cur.cache.clip.r * r2) / 255,
5805                                      (obj->cur.cache.clip.g * g2) / 255,
5806                                      (obj->cur.cache.clip.b * b2) / 255,
5807                                      (obj->cur.cache.clip.a * a2) / 255);
5808              ENFN->rectangle_draw(output,
5809                                   context,
5810                                   surface,
5811                                   obj->cur.geometry.x + ln->x + line2x + x,
5812                                   obj->cur.geometry.y + ln->y + y + ln->baseline + 3,
5813 ////                              obj->cur.cache.geometry.x + ln->x + line2x + x,
5814 ////                              obj->cur.cache.geometry.y + ln->y + y + ln->baseline + 3,
5815                                   x2 - line2x,
5816                                   1);
5817           }
5818         if (it->format->underline2) pline2 = 1;
5819         line2x = it->x;
5820         r2 = it->format->color.underline2.r;
5821         g2 = it->format->color.underline2.g;
5822         b2 = it->format->color.underline2.b;
5823         a2 = it->format->color.underline2.a;
5824      }
5825    ITEM_WALK_END();
5826 }
5827
5828 static void
5829 evas_object_textblock_render_pre(Evas_Object *obj)
5830 {
5831    Evas_Object_Textblock *o;
5832    int is_v, was_v;
5833
5834    /* dont pre-render the obj twice! */
5835    if (obj->pre_render_done) return;
5836    obj->pre_render_done = 1;
5837    /* pre-render phase. this does anything an object needs to do just before */
5838    /* rendering. this could mean loading the image data, retrieving it from */
5839    /* elsewhere, decoding video etc. */
5840    /* then when this is done the object needs to figure if it changed and */
5841    /* if so what and where and add the appropriate redraw textblocks */
5842    o = (Evas_Object_Textblock *)(obj->object_data);
5843    if ((o->changed) ||
5844        (o->last_w != obj->cur.geometry.w))
5845      {
5846         Evas_Object_Textblock_Line *lines;
5847
5848         lines = o->lines;
5849         o->lines = NULL;
5850         o->formatted.valid = 0;
5851         o->native.valid = 0;
5852         _layout(obj,
5853                 0,
5854                 obj->cur.geometry.w, obj->cur.geometry.h,
5855                 &o->formatted.w, &o->formatted.h);
5856         o->formatted.valid = 1;
5857         if (lines) _lines_clear(obj, lines);
5858         o->last_w = obj->cur.geometry.w;
5859         o->redraw = 0;
5860         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
5861         o->changed = 0;
5862         is_v = evas_object_is_visible(obj);
5863         was_v = evas_object_was_visible(obj);
5864         goto done;
5865      }
5866    if (o->redraw)
5867      {
5868         o->redraw = 0;
5869         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
5870         o->changed = 0;
5871         is_v = evas_object_is_visible(obj);
5872         was_v = evas_object_was_visible(obj);
5873         goto done;
5874      }
5875    /* if someone is clipping this obj - go calculate the clipper */
5876    if (obj->cur.clipper)
5877      {
5878         if (obj->cur.cache.clip.dirty)
5879           evas_object_clip_recalc(obj->cur.clipper);
5880         obj->cur.clipper->func->render_pre(obj->cur.clipper);
5881      }
5882    /* now figure what changed and add draw rects */
5883    /* if it just became visible or invisible */
5884    is_v = evas_object_is_visible(obj);
5885    was_v = evas_object_was_visible(obj);
5886    if (is_v != was_v)
5887      {
5888         evas_object_render_pre_visible_change(&obj->layer->evas->clip_changes, obj, is_v, was_v);
5889         goto done;
5890      }
5891    if ((obj->cur.map != obj->prev.map) ||
5892        (obj->cur.usemap != obj->prev.usemap))
5893      {
5894         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
5895         goto done;
5896      }
5897    /* it's not visible - we accounted for it appearing or not so just abort */
5898    if (!is_v) goto done;
5899    /* clipper changed this is in addition to anything else for obj */
5900    evas_object_render_pre_clipper_change(&obj->layer->evas->clip_changes, obj);
5901    /* if we restacked (layer or just within a layer) and don't clip anyone */
5902    if (obj->restack)
5903      {
5904         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
5905         goto done;
5906      }
5907    /* if it changed color */
5908    if ((obj->cur.color.r != obj->prev.color.r) ||
5909        (obj->cur.color.g != obj->prev.color.g) ||
5910        (obj->cur.color.b != obj->prev.color.b) ||
5911        (obj->cur.color.a != obj->prev.color.a))
5912      {
5913         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
5914         goto done;
5915      }
5916    /* if it changed geometry - and obviously not visibility or color */
5917    /* caluclate differences since we have a constant color fill */
5918    /* we really only need to update the differences */
5919    if ((obj->cur.geometry.x != obj->prev.geometry.x) ||
5920        (obj->cur.geometry.y != obj->prev.geometry.y) ||
5921        (obj->cur.geometry.w != obj->prev.geometry.w) ||
5922        (obj->cur.geometry.h != obj->prev.geometry.h))
5923      {
5924         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
5925         goto done;
5926      }
5927    if (o->changed)
5928      {
5929         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
5930         o->changed = 0;
5931      }
5932    done:
5933    evas_object_render_pre_effect_updates(&obj->layer->evas->clip_changes, obj, is_v, was_v);
5934 }
5935
5936 static void
5937 evas_object_textblock_render_post(Evas_Object *obj)
5938 {
5939 /*   Evas_Object_Textblock *o; */
5940
5941    /* this moves the current data to the previous state parts of the object */
5942    /* in whatever way is safest for the object. also if we don't need object */
5943    /* data anymore we can free it if the object deems this is a good idea */
5944 /*   o = (Evas_Object_Textblock *)(obj->object_data); */
5945    /* remove those pesky changes */
5946    evas_object_clip_changes_clean(obj);
5947    /* move cur to prev safely for object data */
5948    obj->prev = obj->cur;
5949 /*   o->prev = o->cur; */
5950 /*   o->changed = 0; */
5951 }
5952
5953 static unsigned int evas_object_textblock_id_get(Evas_Object *obj)
5954 {
5955    Evas_Object_Textblock *o;
5956
5957    o = (Evas_Object_Textblock *)(obj->object_data);
5958    if (!o) return 0;
5959    return MAGIC_OBJ_TEXTBLOCK;
5960 }
5961
5962 static unsigned int evas_object_textblock_visual_id_get(Evas_Object *obj)
5963 {
5964    Evas_Object_Textblock *o;
5965
5966    o = (Evas_Object_Textblock *)(obj->object_data);
5967    if (!o) return 0;
5968    return MAGIC_OBJ_CUSTOM;
5969 }
5970
5971 static void *evas_object_textblock_engine_data_get(Evas_Object *obj)
5972 {
5973    Evas_Object_Textblock *o;
5974
5975    o = (Evas_Object_Textblock *)(obj->object_data);
5976    if (!o) return NULL;
5977    return o->engine_data;
5978 }
5979
5980 static int
5981 evas_object_textblock_is_opaque(Evas_Object *obj)
5982 {
5983    /* this returns 1 if the internal object data implies that the object is */
5984    /* currently fulyl opque over the entire gradient it occupies */
5985    return 0;
5986 }
5987
5988 static int
5989 evas_object_textblock_was_opaque(Evas_Object *obj)
5990 {
5991    /* this returns 1 if the internal object data implies that the object was */
5992    /* currently fulyl opque over the entire gradient it occupies */
5993    return 0;
5994 }
5995
5996 static void
5997 evas_object_textblock_coords_recalc(Evas_Object *obj)
5998 {
5999    Evas_Object_Textblock *o;
6000
6001    o = (Evas_Object_Textblock *)(obj->object_data);
6002    if (obj->cur.geometry.w != o->last_w)
6003      {
6004         o->formatted.valid = 0;
6005         o->native.valid = 0;
6006         o->changed = 1;
6007      }
6008 }
6009
6010 static void
6011 evas_object_textblock_scale_update(Evas_Object *obj)
6012 {
6013    _relayout(obj);
6014 }
6015
6016 void
6017 _evas_object_textblock_rehint(Evas_Object *obj)
6018 {
6019    Evas_Object_Textblock *o;
6020    Evas_Object_Textblock_Line *ln;
6021
6022    o = (Evas_Object_Textblock *)(obj->object_data);
6023    EINA_INLIST_FOREACH(o->lines, ln)
6024      {
6025         Evas_Object_Textblock_Item *it;
6026
6027         EINA_INLIST_FOREACH(ln->items, it)
6028           {
6029              if (it->format->font.font)
6030             {  
6031 #ifdef EVAS_FRAME_QUEUING
6032                evas_common_pipe_op_text_flush(it->format->font.font);
6033 #endif
6034                   evas_font_load_hinting_set(obj->layer->evas,
6035                                           it->format->font.font,
6036                                           obj->layer->evas->hinting);
6037                }
6038           }
6039      }
6040    o->formatted.valid = 0;
6041    o->native.valid = 0;
6042    o->changed = 1;
6043    evas_object_change(obj);
6044 }