1 /**
2  * @internal
3  * @subsection Evas_Object_Textblock_Internal Internal Textblock Object Tutorial
4  *
5  * This explains the internal design of the Evas Textblock Object, it's assumed
6  * that the reader of this section has already read @ref Evas_Object_Textblock_Tutorial "Textblock's usage docs.".
7  *
8  * @subsection textblock_internal_intro Introduction
9  * There are two main parts to the textblock object, the first being the node
10  * system, and the second being the layout system. The former is just an
11  * internal representation of the markup text, while the latter is the internal
12  * visual representation of the text (i.e positioning, sizing, fonts and etc).
13  *
14  * @subsection textblock_nodes The Nodes system
15  * The nodes mechanism consists of two main data types:
16  * ::Evas_Object_Textblock_Node_Text and ::Evas_Object_Textblock_Node_Format
17  * the former is for Text nodes and the latter is for format nodes.
18  * There's always at least one text node, even if there are only formats.
19  *
20  * @subsection textblock_nodes_text Text nodes
21  * Each text node is essentially a paragraph, it includes an @ref Eina_UStrbuf
22  * that stores the actual paragraph text, a utf8 string to store the paragraph
23  * text in utf8 (which is not used internally at all), A pointer to it's
24  * main @ref textblock_nodes_format_internal "Format Node" and the paragraph's
25  * @ref evas_bidi_props "BiDi properties". The pointer to the format node may be
26  * NULL if there's no format node anywhere before the end of the text node,
27  * not even in previous text nodes. If not NULL, it points to the first format
28  * node pointing to text inside of the text node, or if there is none, it points
29  * to the previous's text nodes format node. Each paragraph has a format node
30  * representing a paragraph separator pointing to it's last position except
31  * for the last paragraph, which has no such constraint. This constraint
32  * happens because text nodes are paragraphs and paragraphs are delimited by
33  * paragraph separators.
34  *
35  * @subsection textblock_nodes_format_internal Format Nodes - Internal
36  * Each format node stores a group of format information, for example the
37  * markup: \<font=Vera,Kochi font_size=10 align=left\> will all be inserted
38  * inside the same format node, although it consists of different formatting
39  * commands.
40  * Each node has a pointer to it's text node, this pointer is NEVER NULL, even
41  * if there's only one format, and no text, a text node is created. Each format
42  * node includes an offset from the last format node of the same text node. For
43  * example, the markup "0<b>12</b>" will create two format nodes, the first
44  * having an offset of 1 and the second an offset of 2. Each format node also
45  * includes a @ref Eina_Strbuf that includes the textual representation of the
46  * format, and a boolean stating if the format is a visible format or not, see
47  * @ref textblock_nodes_format_visible
48  *
49  * @subsection textblock_nodes_format_visible Visible Format Nodes
50  * There are two types of format nodes, visible and invisible. They are the same
51  * in every way, except for the representation in the text node. While invisible
52  * format nodes have no representation in the text node, the visible ones do.
53  * The Uniceode object replacement character (0xFFFC) is inserted to every place
54  * a visible format node points to. This makes it very easy to treat visible
55  * formats as items in the text, both for BiDi purposes and cursor handling
56  * purposes.
57  * Here are a few example visible an invisible formats:
58  * Visible: newline char, tab, paragraph separator and an embedded item.
59  * Invisible: setting the color, font or alignment of the text.
60  *
61  * @subsection textblock_layout The layout system
62  * @todo write @ref textblock_layout
63  */
64 
65 #define EFL_CANVAS_FILTER_INTERNAL_PROTECTED
66 
67 #include "evas_common_private.h"
68 #include "evas_private.h"
69 #include "efl_text_cursor_object.eo.h"
70 #include "Efl.h"
71 #include "efl_canvas_textblock_internal.h"
72 
73 //#define LYDBG(f, args...) printf(f, ##args)
74 #define LYDBG(f, args...)
75 
76 #define MY_CLASS EFL_CANVAS_TEXTBLOCK_CLASS
77 
78 #define MY_CLASS_NAME "Efl Canvas Text"
79 
80 #include "linebreak.h"
81 #include "wordbreak.h"
82 #include "graphemebreak.h"
83 
84 #include "evas_filter.h"
85 #include "efl_canvas_filter_internal.eo.h"
86 
87 /* private magic number for textblock objects */
88 static const char o_type[] = "textblock";
89 
90 /* The char to be inserted instead of visible formats */
91 #define _REPLACEMENT_CHAR 0xFFFC
92 #define _PARAGRAPH_SEPARATOR 0x2029
93 #define _NEWLINE '\n'
94 #define _TAB '\t'
95 
96 #define EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(ch) \
97    (((ch) == _REPLACEMENT_CHAR) || \
98     ((ch) ==  _NEWLINE) || \
99     ((ch) == _TAB) || \
100     ((ch) == _PARAGRAPH_SEPARATOR))
101 
102 #ifdef CRI
103 #undef CRI
104 #endif
105 #define CRI(...) EINA_LOG_DOM_CRIT(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__)
106 
107 #ifdef ERR
108 #undef ERR
109 #endif
110 #define ERR(...) EINA_LOG_DOM_ERR(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__)
111 
112 #ifdef WRN
113 #undef WRN
114 #endif
115 #define WRN(...) EINA_LOG_DOM_WARN(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__)
116 
117 #ifdef INF
118 #undef INF
119 #endif
120 #define INF(...) EINA_LOG_DOM_INFO(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__)
121 
122 #ifdef DBG
123 #undef DBG
124 #endif
125 #define DBG(...) EINA_LOG_DOM_DBG(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__)
126 
127 #define TB_NULL_CHECK(null_check, ...) \
128    do \
129      { \
130         if (!null_check) \
131           { \
132              ERR("%s is NULL while it shouldn't be, please notify developers.", #null_check); \
133              return __VA_ARGS__; \
134           } \
135      } \
136    while(0)
137 
138 // testing out some macros to maybe add to eina
139 #define EINA_INLIST_REMOVE(l,i) do { l = (__typeof__(l)) eina_inlist_remove(EINA_INLIST_GET(l), EINA_INLIST_GET(i)); } while (0)
140 #define EINA_INLIST_APPEND(l,i) do { l = (__typeof__(l)) eina_inlist_append(EINA_INLIST_GET(l), EINA_INLIST_GET(i)); } while (0)
141 
142 /**
143  * @internal
144  * @typedef TEXT_FIT_CONTENT_CONFIG
145  * Configurations used to fit content inside Textblock
146  */
147 typedef struct _TEXT_FIT_CONTENT_CONFIG TEXT_FIT_CONTENT_CONFIG;
148 
149 /**
150  * @internal
151  * @def IS_AT_END(ti, ind)
152  * Return true if ind is at the end of the text item, false otherwise.
153  */
154 #define IS_AT_END(ti, ind) (ind == ti->text_props.text_len)
155 
156 /**
157  * @internal
158  * @def MOVE_PREV_UNTIL(limit, ind)
159  * This decrements ind as long as ind > limit.
160  */
161 #define MOVE_PREV_UNTIL(limit, ind) \
162    do \
163      { \
164         if ((limit) < (ind)) \
165            (ind)--; \
166      } \
167    while (0)
168 
169 /**
170  * @internal
171  * @def MOVE_NEXT_UNTIL(limit, ind)
172  * This increments ind as long as ind < limit
173  */
174 #define MOVE_NEXT_UNTIL(limit, ind) \
175    do \
176      { \
177         if ((ind) < (limit)) \
178            (ind)++; \
179      } \
180    while (0)
181 
182 /**
183  * @internal
184  * @def GET_ITEM_LEN(it)
185  * Returns length of item (Format or Text)
186  */
187 #define GET_ITEM_LEN(it) \
188    (((it)->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? \
189     _ITEM_TEXT(it)->text_props.text_len : 1)
190 
191 /**
192  * @internal
193  * @def GET_ITEM_TEXT(ti)
194  * Returns a const reference to the text of the ti (not null terminated).
195  */
196 #define GET_ITEM_TEXT(ti) \
197    (((ti)->parent.text_node) ? \
198     (eina_ustrbuf_string_get((ti)->parent.text_node->unicode) + \
199       (ti)->parent.text_pos) : EINA_UNICODE_EMPTY_STRING)
200 /**
201  * @internal
202  * @def _FORMAT_IS_CLOSER_OF(base, closer, closer_len)
203  * Returns true if closer is the closer of base.
204  */
205 #define _FORMAT_IS_CLOSER_OF(base, closer, closer_len) \
206    (!strncmp(base, closer, closer_len) && \
207     (!base[closer_len] || \
208      (base[closer_len] == '=') || \
209      _is_white(base[closer_len])))
210 
211 
212 /* The default tags to use */
213 static const Evas_Object_Style_Tag_Base default_tags[] = {
214           { "b", "+ font_weight=Bold", 1 },
215           { "i", "+ font_style=Italic", 1 }};
216 
217 #define ANCHOR_NONE 0
218 #define ANCHOR_A 1
219 #define ANCHOR_ITEM 2
220 
221 /**
222  * @internal
223  * @def _NODE_TEXT(x)
224  * A convenience macro for casting to a text node.
225  */
226 #define _NODE_TEXT(x)  ((Evas_Object_Textblock_Node_Text *) (x))
227 /**
228  * @internal
229  * @def _NODE_FORMAT(x)
230  * A convenience macro for casting to a format node.
231  */
232 #define _NODE_FORMAT(x)  ((Evas_Object_Textblock_Node_Format *) (x))
233 /**
234  * @internal
235  * @def _ITEM(x)
236  * A convenience macro for casting to a generic item.
237  */
238 #define _ITEM(x)  ((Evas_Object_Textblock_Item *) (x))
239 /**
240  * @internal
241  * @def _ITEM_TEXT(x)
242  * A convenience macro for casting to a text item.
243  */
244 #define _ITEM_TEXT(x)  ((Evas_Object_Textblock_Text_Item *) (x))
245 /**
246  * @internal
247  * @def _ITEM_FORMAT(x)
248  * A convenience macro for casting to a format item.
249  */
250 #define _ITEM_FORMAT(x)  ((Evas_Object_Textblock_Format_Item *) (x))
251 
252 struct _Evas_Object_Textblock_Paragraph
253 {
254    EINA_INLIST;
255    Evas_Object_Textblock_Line        *lines;  /**< Points to the first line of this paragraph. */
256    Evas_Object_Textblock_Node_Text   *text_node;  /**< Points to the first text node of this paragraph. */
257    Eina_List                         *logical_items;  /**< Logical items are the properties of this paragraph, like width, height etc. */
258    Evas_BiDi_Paragraph_Props         *bidi_props; /**< Only valid during layout. */
259    Evas_BiDi_Direction                direction;  /**< Bidi direction enum value. The display direction like right to left.*/
260    Evas_Coord                         y, w, h;  /**< Text block co-ordinates. y co-ord, width and height. */
261    Evas_Coord                         last_fw;   /**< Last calculated formatted width  */
262    int                                line_no;  /**< Line no of the text block. */
263    Eina_Bool                          is_bidi : 1;  /**< EINA_TRUE if this is BiDi Paragraph, else EINA_FALSE. */
264    Eina_Bool                          visible : 1;  /**< EINA_TRUE if paragraph visible, else EINA_FALSE. */
265    Eina_Bool                          rendered : 1;  /**< EINA_TRUE if paragraph rendered, else EINA_FALSE. */
266 };
267 
268 struct _Evas_Object_Textblock_Line
269 {
270    EINA_INLIST;
271    Evas_Object_Textblock_Item        *items;  /**< Pointer to layouting text item. Contains actual text and information about its display. */
272    Evas_Object_Textblock_Paragraph   *par;  /**< Points to the paragraph of which this line is a part. */
273    Evas_Coord                         x, y, w, h;  /**< Text block line co-ordinates. */
274    int                                baseline;  /**< Baseline of the textblock. */
275    int                                line_no;  /**< Line no of this line. */
276 };
277 
278 typedef enum _Evas_Textblock_Item_Type
279 {
280    EVAS_TEXTBLOCK_ITEM_TEXT,
281    EVAS_TEXTBLOCK_ITEM_FORMAT,
282 } Evas_Textblock_Item_Type;
283 
284 typedef enum _Evas_Textblock_Align_Auto
285 {
286    EVAS_TEXTBLOCK_ALIGN_AUTO_NONE,
287    EVAS_TEXTBLOCK_ALIGN_AUTO_NORMAL,
288    EVAS_TEXTBLOCK_ALIGN_AUTO_LOCALE,
289    EVAS_TEXTBLOCK_ALIGN_AUTO_END
290 } Evas_Textblock_Align_Auto;
291 
292 struct _Evas_Object_Textblock_Item
293 {
294    EINA_INLIST;
295    Evas_Object_Textblock_Node_Text     *text_node;  /**< Pointer to textblock node text. It contains actual text in unicode and utf8 format. */
296    Evas_Object_Textblock_Format        *format;  /**< Pointer to textblock format. It contains all the formatting information for this text block. */
297    Evas_Object_Textblock_Line          *ln;  /**< Pointer to textblock line. It contains the co-ord, baseline, and line no for this item. */
298    size_t                               text_pos;  /**< Position of this item in textblock line. */
299 #ifdef BIDI_SUPPORT
300    size_t                               visual_pos;  /**< Visual position of this item. */
301 #endif
302    Evas_Textblock_Item_Type             type;  /**< EVAS_TEXTBLOCK_ITEM_TEXT or EVAS_TEXTBLOCK_ITEM_FORMAT */
303 
304    Evas_Coord                           adv, x, w, h;  /**< Item co-ordinates. Advancement to be made, x co-ord, width and height. */
305    Evas_Coord                           yoff;  /**< y offset. */
306    Eina_Bool                            merge : 1; /**< Indicates whether this item should merge to the previous item or not */
307    Eina_Bool                            visually_deleted : 1; /**< Indicates whether this item is used in the visual layout or not. */
308 };
309 
310 struct _Evas_Object_Textblock_Text_Item
311 {
312    Evas_Object_Textblock_Item       parent;  /**< Textblock item. */
313    Evas_Text_Props                  text_props;  /**< Props for this item. */
314    Text_Item_Filter                *gfx_filter;
315 };
316 
317 struct _Evas_Object_Textblock_Format_Item
318 {
319    Evas_Object_Textblock_Item           parent;  /**< Textblock item. */
320    Evas_BiDi_Direction                  bidi_dir;  /**< Bidi text direction. */
321    const char                          *item;  /**< Pointer to item contents. */
322    int                                  y;  /**< Co-ordinate of item. */
323    unsigned char                        vsize : 2;  /**< VSIZE_FULL or VSIZE_ASCENT */
324    unsigned char                        size : 2;  /**< SIZE, SIZE_ABS or SIZE_REL*/
325    Eina_Bool                            formatme : 1;  /**< EINA_TRUE if format required, else EINA_FALSE */
326 };
327 
328 struct _Text_Item_Filter
329 {
330    EINA_INLIST; /**< list on the tb object */
331    Efl_Canvas_Textblock_Data            *textblock;
332    Evas_Object_Textblock_Text_Item *ti; /**< associated text item. if null, it was deleted */
333    Evas_Filter_Context             *ctx; /**< running context for the filter */
334    Evas_Public_Data                *evas; /**< evas instance */
335    void                            *output; /**< output rgba buffer for this text item (engine image) */
336    Eina_Bool                        do_async; /**< do_async flag when running the filter */
337 };
338 
339 struct _Efl_Canvas_Textblock_Filter
340 {
341    Eina_Stringshare     *name;
342    Evas_Object          *eo_obj;
343    Evas_Public_Data     *evas;
344    void                 *dc; /* draw context - no clip, white, no colmul... */
345    Evas_Filter_Padding   pad;
346    Eina_Bool             invalid;
347    Eina_Bool             redraw;
348 };
349 
350 struct _Efl_Canvas_Textblock_Filter_Post_Render
351 {
352    Evas_Filter_Context    *ctx;
353    Eina_Bool               success;
354 };
355 
356 struct _Efl_Canvas_Textblock_Filter_Program
357 {
358    EINA_INLIST;
359    Eina_Stringshare    *name;
360    Eina_Stringshare    *code;
361    Evas_Filter_Program *pgm;
362    Eina_Bool            changed;
363 };
364 
365 struct _Evas_Object_Textblock_Format
366 {
367    Evas_Object_Textblock_Node_Format *fnode;  /**< Pointer to textblock format node. */
368    double               halign;  /**< Horizontal alignment value. */
369    double               valign;  /**< Vertical alignment value. */
370    struct {
371       Evas_Font_Description *fdesc;  /**< Pointer to font description. */
372       const char       *source;  /**< Pointer to object from which to search for the font. */
373       Evas_Font_Set    *font;  /**< Pointer to font set. */
374       Evas_Font_Size    size;  /**< Size of the font. */
375       Efl_Text_Font_Bitmap_Scalable bitmap_scalable;  /**< Scalable for bitmap font. */
376    } font;
377    struct {
378       struct {
379          unsigned char  r, g, b, a;
380       } normal, underline, underline2, underline_dash, outline, shadow, glow, glow2, backing,
381         strikethrough;
382    } color;
383    struct {
384       int               l, r;
385    } margin;  /**< Left and right margin width. */
386    Efl_Canvas_Textblock_Filter *gfx_filter; /**< Gfx Filter to apply to the children text items */
387    int                  ref;  /**< Value of the ref. */
388    int                  tabstops;  /**< Value of the size of the tab character. */
389    int                  linesize;  /**< Value of the size of the line of the text. */
390    int                  linegap;  /**< Value to set the line gap in text. */
391    int                  underline_dash_width;  /**< Valule to set the width of the underline dash. */
392    int                  underline_dash_gap;  /**< Value to set the gap of the underline dash. */
393    double               underline_height;  /**< Value to set the height of the single underline. */
394    double               linerelsize;  /**< Value to set the size of line of text. */
395    double               linerelgap;  /**< Value for setting line gap. */
396    double               linefill;  /**< The value must be a percentage. */
397    double               ellipsis;  /**< The value should be a number. Any value smaller than 0.0 or greater than 1.0 disables ellipsis. A value of 0 means ellipsizing the leftmost portion of the text first, 1 on the other hand the rightmost portion. */
398    unsigned char        style;  /**< Value from Evas_Text_Style_Type enum. */
399    Eina_Bool            wrap_word : 1;  /**< EINA_TRUE if only wraps lines at word boundaries, else EINA_FALSE. */
400    Eina_Bool            wrap_char : 1;  /**< EINA_TRUE if wraps at any character, else EINA_FALSE. */
401    Eina_Bool            wrap_mixed : 1;  /**< EINA_TRUE if wrap at words if possible, else EINA_FALSE. */
402    Eina_Bool            wrap_hyphenation : 1;  /**< EINA_TRUE if wrap at mixed and hyphenate if possible, else EINA_FALSE. */
403    Eina_Bool            underline : 1;  /**< EINA_TRUE if a single line under the text, else EINA_FALSE */
404    Eina_Bool            underline2 : 1;  /**< EINA_TRUE if two lines under the text, else EINA_FALSE */
405    Eina_Bool            underline_dash : 1;  /**< EINA_TRUE if a dashed line under the text, else EINA_FALSE */
406    Eina_Bool            strikethrough : 1;  /**< EINA_TRUE if text should be stricked off, else EINA_FALSE */
407    Eina_Bool            backing : 1;  /**< EINA_TRUE if enable background color, else EINA_FALSE */
408    Eina_Bool            password : 1;  /**< EINA_TRUE if the text is password, else EINA_FALSE */
409    Evas_Textblock_Align_Auto halign_auto : 2;  /**< Auto horizontal align mode */
410 };
411 
412 struct _Efl_Canvas_Textblock_Style
413 {
414    const char            *style_text;
415    const char            *default_tag;
416    Evas_Object_Style_Tag *tags;
417    Eina_List             *objects;
418    Eina_Bool              delete_me : 1;
419    Eina_Bool              legacy : 1;
420 };
421 
422 typedef struct _User_Style_Entry
423 {
424    Evas_Textblock_Style *st;
425    const char *key;
426 } User_Style_Entry;
427 
428 struct _TEXT_FIT_CONTENT_CONFIG
429 {
430       unsigned int options;
431       unsigned int min_font_size,max_font_size;
432       unsigned int step_size;
433       unsigned int *p_size_array;
434       size_t size_list_length;
435       Eina_Size2D size_cache[256+1]; /** used hash font sizes 1-255 */
436       Eina_Size2D last_size;
437       int last_size_index;
438       Eina_Bool force_refit;
439       char fit_style[256];
440 };
441 
442 #define _FMT(x) (o->default_format.format.x)
443 #define _FMT_INFO(x) (o->default_format.info.x)
444 
445 /* Size of the index array */
446 #define TEXTBLOCK_PAR_INDEX_SIZE 10
447 
448 #define ASYNC_BLOCK do { \
449    if (o->layout_th) \
450      { \
451         ecore_thread_wait(o->layout_th, 1); \
452      }} while(0)
453 
454 #include "Ecore.h"
455 
456 struct _Evas_Object_Textblock
457 {
458    Ecore_Thread                       *layout_th;
459    int                                 layout_jobs;
460    Evas_Textblock_Style               *style;
461    Eina_List                          *styles;
462    Efl_Text_Cursor_Handle             *cursor;
463    Eina_List                          *cursors;
464    Evas_Object_Textblock_Node_Text    *text_nodes;
465    Evas_Object_Textblock_Node_Format  *format_nodes;
466 
467    int                                 num_paragraphs;
468    Evas_Object_Textblock_Paragraph    *paragraphs;
469    Evas_Object_Textblock_Paragraph    *par_index[TEXTBLOCK_PAR_INDEX_SIZE];
470 
471    Evas_Object_Textblock_Text_Item    *ellip_ti;
472    Eina_List                          *anchors_a;
473    Eina_List                          *anchors_item;
474    Eina_List                          *obstacles;
475    Eina_List                          *hyphen_items; /* Hyphen items storage to free when clearing lines */
476    Efl_Text_Attribute_Handle          *annotations; /* All currently applied annotations on the text. */
477    int                                 last_w, last_h;
478    struct {
479       int                              l, r, t, b;
480    } style_pad;
481    struct {
482       Evas_Object_Textblock_Format   format;
483       struct {
484          Eina_Stringshare               *font;
485          Evas_Font_Size                  size;
486          Eina_Stringshare               *font_source;
487          Eina_Stringshare               *font_fallbacks;
488          Eina_Stringshare               *font_lang;
489          Eina_Stringshare               *gfx_filter_name;
490          unsigned int                    font_weight;
491          unsigned int                    font_slant;
492          unsigned int                    font_width;
493          Efl_Text_Style_Effect_Type      effect;
494          Efl_Text_Style_Shadow_Direction shadow_direction;
495          Efl_Text_Format_Wrap            wrap;
496          Efl_Text_Font_Bitmap_Scalable   bitmap_scalable;
497       } info;
498       char * default_style_str;
499    } default_format;
500    double                              valign;
501    Eina_Stringshare                   *markup_text;
502    Evas_Object_Textblock_Format       *main_fmt;
503    char                               *utf8;
504    void                               *engine_data;
505    const char                         *repch;
506    const char                         *bidi_delimiters;
507    Evas_BiDi_Direction                 paragraph_direction : 2;
508    struct {
509       int                              w, h, oneline_h;
510       Eina_Bool                        valid : 1;
511    } formatted, native;
512    struct {
513       Efl_Canvas_Textblock_Filter_Program  *programs;
514       Evas_Filter_Data_Binding        *data_bindings;
515       Eina_Hash                       *sources;
516       Text_Item_Filter                *text_items; // inlist
517    } gfx_filter;
518    TEXT_FIT_CONTENT_CONFIG             fit_content_config;
519    Eina_Bool                           redraw : 1;
520    Eina_Bool                           changed : 1;
521    Eina_Bool                           pause_change : 1;
522    Eina_Bool                           obstacle_changed : 1;
523    Eina_Bool                           content_changed : 1;
524    Eina_Bool                           format_changed : 1;
525    Eina_Bool                           have_ellipsis : 1;
526    Eina_Bool                           hyphenating : 1;
527    Eina_Bool                           legacy_newline : 1;
528    Eina_Bool                           inherit_paragraph_direction : 1;
529    Eina_Bool                           changed_paragraph_direction : 1;
530    Eina_Bool                           multiline : 1;
531    Eina_Bool                           wrap_changed : 1;
532    Eina_Bool                           auto_styles : 1;
533    Eina_Bool                           fit_in_progress : 1;
534    Eina_Bool                           is_legacy : 1;
535 };
536 
537 struct _Evas_Textblock_Selection_Iterator
538 {
539    Eina_Iterator                       iterator; /**< Eina Iterator. */
540    Eina_List                           *list; /**< Head of list. */
541    Eina_List                           *current; /**< Current node in loop. */
542 };
543 
544 struct _Efl_Text_Attribute_Handle_Iterator
545 {
546    Eina_Iterator                       iterator; /**< Eina Iterator. */
547    Eina_List                           *list; /**< Head of list. */
548    Eina_List                           *current; /**< Current node in loop. */
549 };
550 
551 /* private methods for textblock objects */
552 static void evas_object_textblock_init(Evas_Object *eo_obj);
553 static void evas_object_textblock_render(Evas_Object *eo_obj,
554                                          Evas_Object_Protected_Data *obj,
555                                          void *type_private_data,
556                                          void *engine, void *output, void *context, void *surface,
557                                          int x, int y, Eina_Bool do_async);
558 static void evas_object_textblock_free(Evas_Object *eo_obj);
559 static void evas_object_textblock_render_pre(Evas_Object *eo_obj,
560                                              Evas_Object_Protected_Data *obj,
561                                              void *type_private_data);
562 static void evas_object_textblock_render_post(Evas_Object *eo_obj,
563                                               Evas_Object_Protected_Data *obj,
564                                               void *type_private_data);
565 static Evas_Object_Textblock_Node_Text *_evas_textblock_node_text_new(void);
566 
567 static void *evas_object_textblock_engine_data_get(Evas_Object *eo_obj);
568 
569 static int evas_object_textblock_is_opaque(Evas_Object *eo_obj,
570                                            Evas_Object_Protected_Data *obj,
571                                            void *type_private_data);
572 static int evas_object_textblock_was_opaque(Evas_Object *eo_obj,
573                                             Evas_Object_Protected_Data *obj,
574                                             void *type_private_data);
575 static void evas_object_textblock_coords_recalc(Evas_Object *eo_obj,
576                                                 Evas_Object_Protected_Data *obj,
577                                                 void *type_private_data);
578 static void _canvas_text_format_changed(Eo *eo_obj, Efl_Canvas_Textblock_Data *o);
579 
580 static const Evas_Object_Func object_func =
581 {
582    /* methods (compulsory) */
583    NULL,
584      evas_object_textblock_render,
585      evas_object_textblock_render_pre,
586      evas_object_textblock_render_post,
587      evas_object_textblock_engine_data_get,
588    /* these are optional. NULL = nothing */
589      NULL,
590      NULL,
591      evas_object_textblock_is_opaque,
592      evas_object_textblock_was_opaque,
593      NULL,
594      NULL,
595      NULL, /*evas_object_textblock_coords_recalc, <- disable - not useful. */
596      NULL,
597      NULL,
598      NULL,
599      NULL // render_prepare
600 };
601 
602 /* the actual api call to add a textblock */
603 
604 #define TB_HEAD() \
605    MAGIC_CHECK(eo_obj, Evas_Object, MAGIC_OBJ); \
606    return; \
607    MAGIC_CHECK_END(); \
608    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
609 
610 #define TB_HEAD_RETURN(x) \
611    MAGIC_CHECK(eo_obj, Evas_Object, MAGIC_OBJ); \
612    return (x); \
613    MAGIC_CHECK_END();
614 static Eina_Bool _evas_textblock_cursor_is_at_the_end(const Efl_Text_Cursor_Handle *cur);
615 static void _evas_textblock_node_text_remove(Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Node_Text *n);
616 static Evas_Object_Textblock_Node_Format *_evas_textblock_cursor_node_format_before_or_at_pos_get(const Efl_Text_Cursor_Handle *cur);
617 static size_t _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt);
618 static void _evas_textblock_node_format_remove(Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Node_Format *n, int visual_adjustment);
619 static void _evas_textblock_node_format_free(Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Node_Format *n);
620 static void _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n);
621 static void _evas_textblock_changed(Efl_Canvas_Textblock_Data *o, Evas_Object *eo_obj);
622 static void _evas_textblock_invalidate_all(Efl_Canvas_Textblock_Data *o);
623 static void _evas_textblock_cursors_update_offset(const Efl_Text_Cursor_Handle *cur, const Evas_Object_Textblock_Node_Text *n, size_t start, int offset);
624 static void _evas_textblock_cursors_set_node(Efl_Canvas_Textblock_Data *o, const Evas_Object_Textblock_Node_Text *n, Evas_Object_Textblock_Node_Text *new_node);
625 
626 static Eina_Bool _evas_textblock_cursor_format_is_visible_get(const Efl_Text_Cursor_Handle *cur);
627 static void _evas_textblock_cursor_at_format_set(Efl_Text_Cursor_Handle *cur, const Evas_Object_Textblock_Node_Format *fmt);
628 static Evas_Filter_Program *_format_filter_program_get(Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Format *fmt);
629 static const char *_textblock_format_node_from_style_tag(Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Node_Format *fnode, const char *format, size_t format_len);
630 #ifdef HAVE_HYPHEN
631 /* Hyphenation */
632 #include "evas_textblock_hyphenation.x"
633 #endif
634 
635 static Eina_Bool _evas_textblock_cursor_format_append(Efl_Text_Cursor_Handle *cur, const char *format, Evas_Object_Textblock_Node_Format **_fnode, Eina_Bool is_item);
636 EAPI Eina_Bool evas_textblock_cursor_eol_get(const Evas_Textblock_Cursor *cur);
637 static Eina_Bool _evas_textblock_cursor_format_is_visible_get(const Efl_Text_Cursor_Handle *cur);
638 static void _find_layout_item_line_match(Evas_Object *eo_obj, Evas_Object_Textblock_Node_Text *n, size_t pos, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Item **itr);
639 static Evas_Object_Textblock_Node_Format *_evas_textblock_cursor_node_format_at_pos_get(const Efl_Text_Cursor_Handle *cur);
640 static int _evas_textblock_cursor_text_prepend(Efl_Text_Cursor_Handle *cur, const char *_text);
641 static void _evas_textblock_cursor_copy(Efl_Text_Cursor_Handle *dst, const Efl_Text_Cursor_Handle *src);
642 static void
643 _textblock_style_generic_set(Evas_Object *eo_obj, Evas_Textblock_Style *ts, const char *key);
644 
645 
646 /*********Internal fitting Functions and Defines*********/
647 int fit_cache_clear(TEXT_FIT_CONTENT_CONFIG *fc,const unsigned int fit_cache_flags);
648 int fit_text_block(Evas_Object *eo_obj);
649 int fit_fill_internal_list(TEXT_FIT_CONTENT_CONFIG *fc);
650 int fit_start_fitting(Evas_Object *eo_obj);
651 int fit_finish_fitting(Evas_Object *eo_obj);
652 Eina_Bool fit_is_fitting(const Evas_Object *eo_obj);
653 const unsigned int FIT_CACHE_CANVAS_SIZE         =    0x0001;
654 const unsigned int FIT_CACHE_INTERNAL_SIZE_ARRAY =    0x0002;
655 const unsigned int FIT_CACHE_FORCE_REFIT         =    0x0004;
656 const unsigned int FIT_CACHE_ALL                 =    0x000F;
657 
658 /** selection iterator */
659 /**
660   * @internal
661   * Returns the value of the current data of list node,
662   * and goes to the next list node.
663   *
664   * @param it the iterator.
665   * @param data the data of the current list node.
666   * @return EINA_FALSE if the current list node does not exists.
667   * Otherwise, returns EINA_TRUE.
668   */
669 static Eina_Bool
_evas_textblock_selection_iterator_next(Evas_Textblock_Selection_Iterator * it,void ** data)670 _evas_textblock_selection_iterator_next(Evas_Textblock_Selection_Iterator *it, void **data)
671 {
672    if (!it->current)
673      return EINA_FALSE;
674 
675    *data = eina_list_data_get(it->current);
676    it->current = eina_list_next(it->current);
677 
678    return EINA_TRUE;
679 }
680 
681 /**
682   * @internal
683   * Gets the iterator container (Eina_List) which created the iterator.
684   * @param it the iterator.
685   * @return A pointer to Eina_List.
686   */
687 static Eina_List *
_evas_textblock_selection_iterator_get_container(Evas_Textblock_Selection_Iterator * it)688 _evas_textblock_selection_iterator_get_container(Evas_Textblock_Selection_Iterator *it)
689 {
690    return it->list;
691 }
692 
693 /**
694   * @internal
695   * Frees the iterator container (Eina_List).
696   * @param it the iterator.
697   */
698 static void
_evas_textblock_selection_iterator_free(Evas_Textblock_Selection_Iterator * it)699 _evas_textblock_selection_iterator_free(Evas_Textblock_Selection_Iterator *it)
700 {
701    Evas_Textblock_Rectangle *tr;
702 
703    EINA_LIST_FREE(it->list, tr)
704      free(tr);
705    EINA_MAGIC_SET(&it->iterator, 0);
706    free(it);
707 }
708 
709 /**
710   * @internal
711   * Creates newly allocated  iterator associated to a list.
712   * @param list The list.
713   * @return If the memory cannot be allocated, NULL is returned.
714   * Otherwise, a valid iterator is returned.
715   */
716 Eina_Iterator *
_evas_textblock_selection_iterator_new(Eina_List * list)717 _evas_textblock_selection_iterator_new(Eina_List *list)
718 {
719    Evas_Textblock_Selection_Iterator *it;
720 
721    it = calloc(1, sizeof(Evas_Textblock_Selection_Iterator));
722    if (!it) return NULL;
723 
724    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
725    it->list = list;
726    it->current = list;
727 
728    it->iterator.version = EINA_ITERATOR_VERSION;
729    it->iterator.next = FUNC_ITERATOR_NEXT(
730                                      _evas_textblock_selection_iterator_next);
731    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
732                             _evas_textblock_selection_iterator_get_container);
733    it->iterator.free = FUNC_ITERATOR_FREE(
734                                      _evas_textblock_selection_iterator_free);
735 
736    return &it->iterator;
737 }
738 
739 /* styles */
740 /**
741  * @internal
742  * Clears the textblock style passed except for the style_text which is replaced.
743  * @param ts The ts to be cleared. Must not be NULL.
744  * @param style_text the style's text.
745  */
746 static void
_style_replace(Evas_Textblock_Style * ts,const char * style_text)747 _style_replace(Evas_Textblock_Style *ts, const char *style_text)
748 {
749    eina_stringshare_replace(&ts->style_text, style_text);
750    if (ts->default_tag) eina_stringshare_del(ts->default_tag);
751    while (ts->tags)
752      {
753         Evas_Object_Style_Tag *tag;
754 
755         tag = (Evas_Object_Style_Tag *)ts->tags;
756         ts->tags = (Evas_Object_Style_Tag *)eina_inlist_remove(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
757         eina_stringshare_del(tag->tag.tag);
758         eina_stringshare_del(tag->tag.replace);
759         free(tag);
760      }
761    ts->default_tag = NULL;
762    ts->tags = NULL;
763 }
764 
765 /**
766  * @internal
767  * Clears the textblock style passed.
768  * @param ts The ts to be cleared. Must not be NULL.
769  */
770 static void
_style_clear(Evas_Textblock_Style * ts)771 _style_clear(Evas_Textblock_Style *ts)
772 {
773    _style_replace(ts, NULL);
774 }
775 
776 /**
777  * @internal
778  * Searches inside the tags stored in the style for the tag matching s.
779  * @param ts The ts to be cleared. Must not be NULL.
780  * @param s The tag to be matched.
781  * @param tag_len the length of the tag string.
782  * @param[out] replace_len The length of the replacement found. - Must not be NULL.
783  * @return The replacement string found.
784  */
785 static inline const char *
_style_match_tag(const Evas_Textblock_Style * ts,const char * s,size_t tag_len)786 _style_match_tag(const Evas_Textblock_Style *ts, const char *s, size_t tag_len)
787 {
788    Evas_Object_Style_Tag *tag;
789 
790    /* Try the style tags */
791    if (ts)
792      {
793         EINA_INLIST_FOREACH(ts->tags, tag)
794           {
795              if (tag->tag.tag_len != tag_len) continue;
796              if (!strncmp(tag->tag.tag, s, tag_len))
797                {
798                   return tag->tag.replace;
799                }
800           }
801      }
802 
803    /* Try the default tags */
804    {
805       size_t i;
806       const Evas_Object_Style_Tag_Base *btag;
807       for (btag = default_tags, i = 0 ;
808             i < (sizeof(default_tags) / sizeof(default_tags[0])) ;
809             btag++, i++)
810         {
811            if (btag->tag_len != tag_len) continue;
812            if (!strncmp(btag->tag, s, tag_len))
813              {
814                 return btag->replace;
815              }
816         }
817    }
818 
819    return NULL;
820 }
821 
822 /**
823  * @internal
824  * Clears all the nodes (text and format) of the textblock object.
825  * @param obj The evas object, must not be NULL.
826  */
827 static void
_nodes_clear(const Evas_Object * eo_obj)828 _nodes_clear(const Evas_Object *eo_obj)
829 {
830    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
831 
832    /* First, clear all annotations that may have spawned format nodes. */
833    _evas_textblock_annotations_clear(eo_obj);
834 
835    while (o->text_nodes)
836      {
837         Evas_Object_Textblock_Node_Text *n;
838 
839         n = o->text_nodes;
840         o->text_nodes = _NODE_TEXT(eina_inlist_remove(
841                  EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n)));
842         _evas_textblock_node_text_free(n);
843      }
844    while (o->format_nodes)
845      {
846         Evas_Object_Textblock_Node_Format *n;
847 
848         n = o->format_nodes;
849         o->format_nodes = _NODE_FORMAT(eina_inlist_remove(EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n)));
850         _evas_textblock_node_format_free(o, n);
851      }
852 }
853 
854 /**
855  * @internal
856  * Unrefs and frees (if needed) a textblock format.
857  * @param obj The Evas_Object, Must not be NULL.
858  * @param fmt the format to be cleaned, must not be NULL.
859  */
860 static void
_format_unref_free(Evas_Object_Protected_Data * evas_o,Evas_Object_Textblock_Format * fmt)861 _format_unref_free(Evas_Object_Protected_Data *evas_o, Evas_Object_Textblock_Format *fmt)
862 {
863    Evas_Object_Protected_Data *obj = evas_o;
864    fmt->ref--;
865    if (fmt->ref > 0) return;
866    if (fmt->font.fdesc) evas_font_desc_unref(fmt->font.fdesc);
867    if (fmt->font.source) eina_stringshare_del(fmt->font.source);
868    if (fmt->gfx_filter)
869      {
870         eina_stringshare_del(fmt->gfx_filter->name);
871         if (fmt->gfx_filter->dc)
872           ENFN->context_free(ENC, fmt->gfx_filter->dc);
873         free(fmt->gfx_filter);
874         fmt->gfx_filter = NULL;
875      }
876    if ((obj->layer) && (obj->layer->evas))
877      evas_font_free(fmt->font.font);
878    free(fmt);
879 }
880 
881 static inline void
_image_safe_unref(Evas_Public_Data * e,void * image,Eina_Bool async)882 _image_safe_unref(Evas_Public_Data *e, void *image, Eina_Bool async)
883 {
884    if (!image) return;
885    if (async)
886      evas_unref_queue_image_put(e, image);
887    else
888      e->engine.func->image_free(_evas_engine_context(e), image);
889 }
890 
891 /**
892  * @internal
893  * Free a layout item
894  * @param obj The evas object, must not be NULL.
895  * @param ln the layout line on which the item is in, must not be NULL.
896  * @param it the layout item to be freed
897  */
898 static void
_item_free(Efl_Canvas_Textblock_Data * o,Evas_Object_Protected_Data * evas_o,Evas_Object_Textblock_Line * ln,Evas_Object_Textblock_Item * it)899 _item_free(Efl_Canvas_Textblock_Data *o,
900       Evas_Object_Protected_Data *evas_o,
901       Evas_Object_Textblock_Line *ln, Evas_Object_Textblock_Item *it)
902 {
903    if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
904      {
905         Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
906 
907         evas_common_text_props_content_unref(&ti->text_props);
908         if (EINA_UNLIKELY(ti->gfx_filter != NULL))
909           {
910              if (ti->gfx_filter->output)
911                {
912                   //Evas *eo_evas = evas_object_evas_get(eo_obj);
913                   //Evas_Public_Data *evas = efl_data_scope_get(eo_evas, EVAS_CANVAS_CLASS);
914                   Eina_Bool async = ti->gfx_filter->do_async;
915 
916                   _image_safe_unref(evas_o->layer->evas, ti->gfx_filter->output, async);
917                   ti->gfx_filter->output = NULL;
918                }
919              EINA_INLIST_REMOVE(o->gfx_filter.text_items, ti->gfx_filter);
920              if (!ti->gfx_filter->ctx)
921                {
922                   free(ti->gfx_filter);
923                   ti->gfx_filter = NULL;
924                }
925              else
926                {
927                   evas_filter_context_unref(ti->gfx_filter->ctx);
928                   ti->gfx_filter->ctx = NULL;
929                   ti->gfx_filter->ti = NULL;
930                }
931           }
932      }
933    else
934      {
935         Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
936 
937         if (fi->item) eina_stringshare_del(fi->item);
938      }
939    _format_unref_free(evas_o, it->format);
940    if (ln)
941      {
942         ln->items = (Evas_Object_Textblock_Item *) eina_inlist_remove(
943               EINA_INLIST_GET(ln->items), EINA_INLIST_GET(it));
944      }
945    free(it);
946 }
947 
948 /**
949  * @internal
950  * Free a layout line.
951  * @param obj The evas object, must not be NULL.
952  * @param ln the layout line to be freed, must not be NULL.
953  */
954 static void
_line_free(Evas_Object_Textblock_Line * ln)955 _line_free(Evas_Object_Textblock_Line *ln)
956 {
957    /* Items are freed from the logical list, except for the ellip item */
958    if (ln) free(ln);
959 }
960 
961 /* table of html escapes (that i can find) this should be ordered with the
962  * sorted by there escape strings and values as it's a binary search to match - no hash for this.
963  *
964  * these are stored as array of struct of Escape_Value structure (no Runtime sort will happen)
965  */
966 
967 
968 typedef struct _Escape_Value Escape_Value;
969 
970 struct _Escape_Value
971 {
972    char *escape;
973    char *value;
974    size_t escape_len;
975    size_t value_len;
976 };
977 
978 #define ESCAPE_VALUE(e,v) {e,v,strlen(e),strlen(v)}
979 
980 /**
981  * @internal
982  * @var html_common_escapes[]
983  * This array consists of most common html escapes values as _Escape_Value structure
984  */
985 static const Escape_Value html_common_escapes[] = {
986    ESCAPE_VALUE("&amp;", "\x26"),
987    ESCAPE_VALUE("&apos;", "\x27"),
988    ESCAPE_VALUE("&gt;", "\x3e"),
989    ESCAPE_VALUE("&lt;", "\x3c"),
990    ESCAPE_VALUE("&quot;", "\x22"),
991 };
992 
993 
994 /**
995  * @internal
996  * @var escape_values_e_common_sorted[]
997  * This array consists of rest html escapes values as _Escape_Value structure
998  */
999 
1000 static const Escape_Value html_escapes[] = {
1001    ESCAPE_VALUE("&Aacute;", "\xc3\x81"),
1002    ESCAPE_VALUE("&Acirc;", "\xc3\x82"),
1003    ESCAPE_VALUE("&Aelig;", "\xc3\x86"),
1004    ESCAPE_VALUE("&Agrave;", "\xc3\x80"),
1005    ESCAPE_VALUE("&Aring;", "\xc3\x85"),
1006    ESCAPE_VALUE("&Atilde;", "\xc3\x83"),
1007    ESCAPE_VALUE("&Auml;", "\xc3\x84"),
1008    ESCAPE_VALUE("&Ccedil;", "\xc3\x87"),
1009    ESCAPE_VALUE("&Dagger;", "\xe2\x80\xa1"),
1010    ESCAPE_VALUE("&Eacute;", "\xc3\x89"),
1011    ESCAPE_VALUE("&Ecirc;", "\xc3\x8a"),
1012    ESCAPE_VALUE("&Egrave;", "\xc3\x88"),
1013    ESCAPE_VALUE("&Eth;", "\xc3\x90"),
1014    ESCAPE_VALUE("&Euml;", "\xc3\x8b"),
1015    ESCAPE_VALUE("&Iacute;", "\xc3\x8d"),
1016    ESCAPE_VALUE("&Icirc;", "\xc3\x8e"),
1017    ESCAPE_VALUE("&Igrave;", "\xc3\x8c"),
1018    ESCAPE_VALUE("&Iuml;", "\xc3\x8f"),
1019    ESCAPE_VALUE("&Ntilde;", "\xc3\x91"),
1020    ESCAPE_VALUE("&Oacute;", "\xc3\x93"),
1021    ESCAPE_VALUE("&Ocirc;", "\xc3\x94"),
1022    ESCAPE_VALUE("&Ograve;", "\xc3\x92"),
1023    ESCAPE_VALUE("&Oslash;", "\xc3\x98"),
1024    ESCAPE_VALUE("&Otilde;", "\xc3\x95"),
1025    ESCAPE_VALUE("&Ouml;", "\xc3\x96"),
1026    ESCAPE_VALUE("&Thorn;", "\xc3\x9e"),
1027    ESCAPE_VALUE("&Uacute;", "\xc3\x9a"),
1028    ESCAPE_VALUE("&Ucirc;", "\xc3\x9b"),
1029    ESCAPE_VALUE("&Ugrave;", "\xc3\x99"),
1030    ESCAPE_VALUE("&Yacute;", "\xc3\x9d"),
1031    ESCAPE_VALUE("&aacute;", "\xc3\xa1"),
1032    ESCAPE_VALUE("&acirc;", "\xc3\xa2"),
1033    ESCAPE_VALUE("&acute;", "\xc2\xb4"),
1034    ESCAPE_VALUE("&aelig;", "\xc3\xa6"),
1035    ESCAPE_VALUE("&agrave;", "\xc3\xa0"),
1036    ESCAPE_VALUE("&alpha;", "\xce\x91"),
1037    ESCAPE_VALUE("&and;", "\xe2\x88\xa7"),
1038    ESCAPE_VALUE("&aring;", "\xc3\xa5"),
1039    ESCAPE_VALUE("&atilde;", "\xc3\xa3"),
1040    ESCAPE_VALUE("&auml;", "\xc3\xa4"),
1041    ESCAPE_VALUE("&beta;", "\xce\x92"),
1042    ESCAPE_VALUE("&brvbar;", "\xc2\xa6"),
1043    ESCAPE_VALUE("&bull;", "\xe2\x80\xa2"),
1044    ESCAPE_VALUE("&ccedil;", "\xc3\xa7"),
1045    ESCAPE_VALUE("&cedil;", "\xc2\xb8"),
1046    ESCAPE_VALUE("&cent;", "\xc2\xa2"),
1047    ESCAPE_VALUE("&chi;", "\xce\xa7"),
1048    ESCAPE_VALUE("&copy;", "\xc2\xa9"),
1049    ESCAPE_VALUE("&curren;", "\xc2\xa4"),
1050    ESCAPE_VALUE("&dagger;", "\xe2\x80\xa0"),
1051    ESCAPE_VALUE("&darr;", "\xe2\x86\x93"),
1052    ESCAPE_VALUE("&deg;", "\xc2\xb0"),
1053    ESCAPE_VALUE("&delta;", "\xce\x94"),
1054    ESCAPE_VALUE("&divide;", "\xc3\xb7"),
1055    ESCAPE_VALUE("&eacute;", "\xc3\xa9"),
1056    ESCAPE_VALUE("&ecirc;", "\xc3\xaa"),
1057    ESCAPE_VALUE("&egrave;", "\xc3\xa8"),
1058    ESCAPE_VALUE("&epsilon;", "\xce\x95"),
1059    ESCAPE_VALUE("&equiv;", "\xe2\x89\xa1"),
1060    ESCAPE_VALUE("&eta;", "\xce\x97"),
1061    ESCAPE_VALUE("&eth;", "\xc3\xb0"),
1062    ESCAPE_VALUE("&euml;", "\xc3\xab"),
1063    ESCAPE_VALUE("&euro;", "\xe2\x82\xac"),
1064    ESCAPE_VALUE("&exist;", "\xe2\x88\x83"),
1065    ESCAPE_VALUE("&forall;", "\xe2\x88\x80"),
1066    ESCAPE_VALUE("&frac12;", "\xc2\xbd"),
1067    ESCAPE_VALUE("&frac14;", "\xc2\xbc"),
1068    ESCAPE_VALUE("&frac34;", "\xc2\xbe"),
1069    ESCAPE_VALUE("&gamma;", "\xce\x93"),
1070    ESCAPE_VALUE("&harr;", "\xe2\x86\x94"),
1071    ESCAPE_VALUE("&hellip;", "\xe2\x80\xa6"),
1072    ESCAPE_VALUE("&iacute;", "\xc3\xad"),
1073    ESCAPE_VALUE("&icirc;", "\xc3\xae"),
1074    ESCAPE_VALUE("&iexcl;", "\xc2\xa1"),
1075    ESCAPE_VALUE("&igrave;", "\xc3\xac"),
1076    ESCAPE_VALUE("&int;", "\xe2\x88\xab"),
1077    ESCAPE_VALUE("&iota;", "\xce\x99"),
1078    ESCAPE_VALUE("&iquest;", "\xc2\xbf"),
1079    ESCAPE_VALUE("&iuml;", "\xc3\xaf"),
1080    ESCAPE_VALUE("&kappa;", "\xce\x9a"),
1081    ESCAPE_VALUE("&lambda;", "\xce\x9b"),
1082    ESCAPE_VALUE("&laquo;", "\xc2\xab"),
1083    ESCAPE_VALUE("&larr;", "\xe2\x86\x90"),
1084    ESCAPE_VALUE("&larr;", "\xe2\x87\x90"),
1085    ESCAPE_VALUE("&lrm;", "\xe2\x80\x8e"),
1086    ESCAPE_VALUE("&macr;", "\xc2\xaf"),
1087    ESCAPE_VALUE("&micro;", "\xc2\xb5"),
1088    ESCAPE_VALUE("&middot;", "\xc2\xb7"),
1089    ESCAPE_VALUE("&mu;", "\xce\x9c"),
1090    ESCAPE_VALUE("&nabla;", "\xe2\x88\x87"),
1091    ESCAPE_VALUE("&nbsp;", "\xc2\xa0"),
1092    ESCAPE_VALUE("&ne;", "\xe2\x89\xa0"),
1093    ESCAPE_VALUE("&not;", "\xc2\xac"),
1094    ESCAPE_VALUE("&ntilde;", "\xc3\xb1"),
1095    ESCAPE_VALUE("&nu;", "\xce\x9d"),
1096    ESCAPE_VALUE("&oacute;", "\xc3\xb3"),
1097    ESCAPE_VALUE("&ocirc;", "\xc3\xb4"),
1098    ESCAPE_VALUE("&ograve;", "\xc3\xb2"),
1099    ESCAPE_VALUE("&omega;", "\xce\xa9"),
1100    ESCAPE_VALUE("&omicron;", "\xce\x9f"),
1101    ESCAPE_VALUE("&oplus;", "\xe2\x8a\x95"),
1102    ESCAPE_VALUE("&or;", "\xe2\x88\xa8"),
1103    ESCAPE_VALUE("&ordf;", "\xc2\xaa"),
1104    ESCAPE_VALUE("&ordm;", "\xc2\xba"),
1105    ESCAPE_VALUE("&oslash;", "\xc3\xb8"),
1106    ESCAPE_VALUE("&otilde;", "\xc3\xb5"),
1107    ESCAPE_VALUE("&ouml;", "\xc3\xb6"),
1108    ESCAPE_VALUE("&para;", "\xc2\xb6"),
1109    ESCAPE_VALUE("&perp;", "\xe2\x8a\xa5"),
1110    ESCAPE_VALUE("&phi;", "\xce\xa6"),
1111    ESCAPE_VALUE("&pi;", "\xce\xa0"),
1112    ESCAPE_VALUE("&plusmn;", "\xc2\xb1"),
1113    ESCAPE_VALUE("&pound;", "\xc2\xa3"),
1114    ESCAPE_VALUE("&prod;", "\xe2\x88\x8f"),
1115    ESCAPE_VALUE("&psi;", "\xce\xa8"),
1116    ESCAPE_VALUE("&raquo;", "\xc2\xbb"),
1117    ESCAPE_VALUE("&rarr;", "\xe2\x86\x92"),
1118    ESCAPE_VALUE("&rArr;", "\xe2\x87\x92"),
1119    ESCAPE_VALUE("&reg;", "\xc2\xae"),
1120    ESCAPE_VALUE("&rho;", "\xce\xa1"),
1121    ESCAPE_VALUE("&rlm;", "\xe2\x80\x8f"),
1122    ESCAPE_VALUE("&sect;", "\xc2\xa7"),
1123    ESCAPE_VALUE("&shy;", "\xc2\xad"),
1124    ESCAPE_VALUE("&sigma;", "\xce\xa3"),
1125    ESCAPE_VALUE("&sum;", "\xe2\x88\x91"),
1126    ESCAPE_VALUE("&sup1;", "\xc2\xb9"),
1127    ESCAPE_VALUE("&sup2;", "\xc2\xb2"),
1128    ESCAPE_VALUE("&sup3;", "\xc2\xb3"),
1129    ESCAPE_VALUE("&szlig;", "\xc3\x9f"),
1130    ESCAPE_VALUE("&tau;", "\xce\xa4"),
1131    ESCAPE_VALUE("&theta;", "\xce\x98"),
1132    ESCAPE_VALUE("&thorn;", "\xc3\xbe"),
1133    ESCAPE_VALUE("&times;", "\xc3\x97"),
1134    ESCAPE_VALUE("&uacute;", "\xc3\xba"),
1135    ESCAPE_VALUE("&uarr;", "\xe2\x86\x91"),
1136    ESCAPE_VALUE("&ucirc;", "\xc3\xbb"),
1137    ESCAPE_VALUE("&ugrave;", "\xc3\xb9"),
1138    ESCAPE_VALUE("&uml;", "\xc2\xa8"),
1139    ESCAPE_VALUE("&upsilon;", "\xce\xa5"),
1140    ESCAPE_VALUE("&uuml;", "\xc3\xbc"),
1141    ESCAPE_VALUE("&xi;", "\xce\x9e"),
1142    ESCAPE_VALUE("&yacute;", "\xc3\xbd"),
1143    ESCAPE_VALUE("&yen;", "\xc2\xa5"),
1144    ESCAPE_VALUE("&yuml;", "\xc3\xbf"),
1145    ESCAPE_VALUE("&zeta;", "\xce\x96"),
1146    ESCAPE_VALUE("&zwj;", "\xe2\x80\x8d"),
1147    ESCAPE_VALUE("&zwnj;", "\xe2\x80\x8c"),
1148 };
1149 
1150 static int
_escape_key_sort(const void * a,const void * b)1151 _escape_key_sort(const void *a, const void *b)
1152 {
1153    const char *k_a = (*(const Escape_Value **) a)->escape;
1154    const char *k_b = (*(const Escape_Value **) b)->escape;
1155    return strcmp(k_a, k_b);
1156 }
1157 
1158 static int
_escape_value_sort(const void * a,const void * b)1159 _escape_value_sort(const void *a, const void *b)
1160 {
1161    const char *v_a = (*(const Escape_Value **) a)->value;
1162    const char *v_b = (*(const Escape_Value **) b)->value;
1163    return strcmp(v_a, v_b);
1164 }
1165 
1166 static Escape_Value **
escape_sorted_common_key_copy()1167 escape_sorted_common_key_copy()
1168 {
1169    int i;
1170    int len;
1171    const Escape_Value *source;
1172    int (*compare_fun)(const void*,const void*);
1173 
1174    len = sizeof(html_common_escapes) / sizeof(Escape_Value);
1175    source = html_common_escapes;
1176 
1177    compare_fun = _escape_key_sort;
1178 
1179    Escape_Value **ret_list = malloc(len * sizeof(Escape_Value *));
1180    for (i = 0 ; i < len ; i++)
1181      {
1182         ret_list[i] = (Escape_Value *)(&source[i]);
1183      }
1184 
1185    qsort(&ret_list[0], len, sizeof(Escape_Value *), compare_fun);
1186    return ret_list;
1187 }
1188 
1189 static Escape_Value **
escape_sorted_common_value_copy()1190 escape_sorted_common_value_copy()
1191 {
1192    int i;
1193    int len;
1194    const Escape_Value *source;
1195    int (*compare_fun)(const void*,const void*);
1196 
1197    len = sizeof(html_common_escapes) / sizeof(Escape_Value);
1198    source = html_common_escapes;
1199 
1200    compare_fun = _escape_value_sort;
1201 
1202    Escape_Value **ret_list = malloc(len * sizeof(Escape_Value *));
1203    for (i = 0 ; i < len ; i++)
1204      {
1205         ret_list[i] = (Escape_Value *)(&source[i]);
1206      }
1207 
1208    qsort(&ret_list[0], len, sizeof(Escape_Value *), compare_fun);
1209    return ret_list;
1210 }
1211 
1212 static Escape_Value **
escape_sorted_rest_key_copy()1213 escape_sorted_rest_key_copy()
1214 {
1215    int i;
1216    int len;
1217    const Escape_Value *source;
1218    int (*compare_fun)(const void*,const void*);
1219 
1220    len = sizeof(html_escapes) / sizeof(Escape_Value);
1221    source = html_escapes;
1222 
1223    compare_fun = _escape_key_sort;
1224 
1225    Escape_Value **ret_list = malloc(len * sizeof(Escape_Value *));
1226    for (i = 0 ; i < len ; i++)
1227      {
1228         ret_list[i] = (Escape_Value *)(&source[i]);
1229      }
1230 
1231    qsort(&ret_list[0], len, sizeof(Escape_Value *), compare_fun);
1232    return ret_list;
1233 }
1234 
1235 static Escape_Value **
escape_sorted_rest_value_copy()1236 escape_sorted_rest_value_copy()
1237 {
1238    int i;
1239    int len;
1240    const Escape_Value *source;
1241    int (*compare_fun)(const void*,const void*);
1242    len = sizeof(html_escapes) / sizeof(Escape_Value);
1243    source = html_escapes;
1244 
1245    compare_fun = _escape_value_sort;
1246 
1247    Escape_Value **ret_list = malloc(len * sizeof(Escape_Value *));
1248    for (i = 0 ; i < len ; i++)
1249      {
1250         ret_list[i] = (Escape_Value *)(&source[i]);
1251      }
1252 
1253    qsort(&ret_list[0], len, sizeof(Escape_Value *), compare_fun);
1254    return ret_list;
1255 }
1256 
1257 static Escape_Value **
get_html_escape_array_common_key_sorted(size_t * p_len)1258 get_html_escape_array_common_key_sorted(size_t *p_len)
1259 {
1260    static Escape_Value **escape_values_common_k_sorted = NULL;
1261 
1262    static size_t common_len = sizeof(html_common_escapes) / sizeof(Escape_Value);
1263 
1264    Escape_Value **ret_list = NULL;
1265 
1266    if (!escape_values_common_k_sorted)
1267      escape_values_common_k_sorted = escape_sorted_common_key_copy();
1268    ret_list = escape_values_common_k_sorted;
1269    if(p_len) *p_len = common_len;
1270 
1271 
1272    return ret_list;
1273 }
1274 
1275 static Escape_Value **
get_html_escape_array_common_value_sorted(size_t * p_len)1276 get_html_escape_array_common_value_sorted(size_t *p_len)
1277 {
1278    static Escape_Value **escape_values_common_v_sorted = NULL;
1279 
1280    static size_t common_len = sizeof(html_common_escapes) / sizeof(Escape_Value);
1281 
1282    Escape_Value **ret_list = NULL;
1283 
1284    if (!escape_values_common_v_sorted)
1285      escape_values_common_v_sorted = escape_sorted_common_value_copy();
1286    ret_list = escape_values_common_v_sorted;
1287    if(p_len) *p_len = common_len;
1288 
1289 
1290    return ret_list;
1291 }
1292 
1293 static Escape_Value **
get_html_escape_array_rest_key_sorted(size_t * p_len)1294 get_html_escape_array_rest_key_sorted(size_t *p_len)
1295 {
1296    static Escape_Value **escape_values_k_sorted = NULL;
1297 
1298    static size_t rest_len = sizeof(html_escapes) / sizeof(Escape_Value);
1299 
1300    Escape_Value **ret_list = NULL;
1301 
1302    if (!escape_values_k_sorted)
1303      escape_values_k_sorted = escape_sorted_rest_key_copy();
1304    ret_list = escape_values_k_sorted;
1305    if(p_len) *p_len = rest_len;
1306 
1307    return ret_list;
1308 }
1309 
1310 static Escape_Value **
get_html_escape_array_rest_value_sorted(size_t * p_len)1311 get_html_escape_array_rest_value_sorted(size_t *p_len)
1312 {
1313    static Escape_Value **escape_values_v_sorted = NULL;
1314 
1315    static size_t rest_len = sizeof(html_escapes) / sizeof(Escape_Value);
1316 
1317    Escape_Value **ret_list = NULL;
1318 
1319    if (!escape_values_v_sorted)
1320      escape_values_v_sorted = escape_sorted_rest_value_copy();
1321    ret_list = escape_values_v_sorted;
1322    if(p_len) *p_len = rest_len;
1323 
1324    return ret_list;
1325 }
1326 
1327 /**
1328  * @internal
1329  * Checks if a char is a whitespace.
1330  * @param c the unicode codepoint.
1331  * @return @c EINA_TRUE if the unicode codepoint is a whitespace, @c EINA_FALSE
1332  * otherwise.
1333  */
1334 static Eina_Bool
_is_white(Eina_Unicode c)1335 _is_white(Eina_Unicode c)
1336 {
1337    /*
1338     * unicode list of whitespace chars
1339     *
1340     * 0009..000D <control-0009>..<control-000D>
1341     * 0020 SPACE
1342     * 0085 <control-0085>
1343     * 00A0 NO-BREAK SPACE
1344     * 1680 OGHAM SPACE MARK
1345     * 180E MONGOLIAN VOWEL SEPARATOR
1346     * 2000..200A EN QUAD..HAIR SPACE
1347     * 2028 LINE SEPARATOR
1348     * 2029 PARAGRAPH SEPARATOR
1349     * 202F NARROW NO-BREAK SPACE
1350     * 205F MEDIUM MATHEMATICAL SPACE
1351     * 3000 IDEOGRAPHIC SPACE
1352     */
1353    if (
1354          (c == 0x20) ||
1355          ((c >= 0x9) && (c <= 0xd)) ||
1356          (c == 0x85) ||
1357          (c == 0xa0) ||
1358          (c == 0x1680) ||
1359          (c == 0x180e) ||
1360          ((c >= 0x2000) && (c <= 0x200a)) ||
1361          (c == 0x2028) ||
1362          (c == 0x2029) ||
1363          (c == 0x202f) ||
1364          (c == 0x205f) ||
1365          (c == 0x3000)
1366       )
1367      return EINA_TRUE;
1368    return EINA_FALSE;
1369 }
1370 
1371 /**
1372  * @internal
1373  * Prepends the text between s and p to the main cursor of the object.
1374  *
1375  * @param cur the cursor to prepend to.
1376  * @param[in] s start of the string
1377  * @param[in] p end of the string
1378  */
1379 static void
_prepend_text_run(Efl_Text_Cursor_Handle * cur,const char * s,const char * p)1380 _prepend_text_run(Efl_Text_Cursor_Handle *cur, const char *s, const char *p)
1381 {
1382    if ((s) && (p > s))
1383      {
1384         char *ts;
1385 
1386         ts = alloca(p - s + 1);
1387         strncpy(ts, s, p - s);
1388         ts[p - s] = 0;
1389         evas_textblock_cursor_text_prepend(cur, ts);
1390      }
1391 }
1392 
1393 /* The refcount for the formats. */
1394 static int format_refcount = 0;
1395 /* Holders for the stringshares */
1396 static const char *fontstr = NULL;
1397 static const char *font_fallbacksstr = NULL;
1398 static const char *font_sizestr = NULL;
1399 static const char *font_sourcestr = NULL;
1400 static const char *font_weightstr = NULL;
1401 static const char *font_stylestr = NULL;
1402 static const char *font_widthstr = NULL;
1403 static const char *langstr = NULL;
1404 static const char *colorstr = NULL;
1405 static const char *underline_colorstr = NULL;
1406 static const char *underline2_colorstr = NULL;
1407 static const char *secondary_underline_colorstr = NULL;
1408 static const char *underline_dash_colorstr = NULL;
1409 static const char *underline_dashed_colorstr = NULL;
1410 static const char *outline_colorstr = NULL;
1411 static const char *shadow_colorstr = NULL;
1412 static const char *glow_colorstr = NULL;
1413 static const char *glow2_colorstr = NULL;
1414 static const char *secondary_glow_colorstr = NULL;
1415 static const char *backing_colorstr = NULL;
1416 static const char *background_colorstr = NULL;
1417 static const char *strikethrough_colorstr = NULL;
1418 static const char *alignstr = NULL;
1419 static const char *valignstr = NULL;
1420 static const char *text_valignstr = NULL;
1421 static const char *wrapstr = NULL;
1422 static const char *left_marginstr = NULL;
1423 static const char *right_marginstr = NULL;
1424 static const char *underlinestr = NULL;
1425 static const char *underline_typestr = NULL;
1426 static const char *strikethroughstr = NULL;
1427 static const char *strikethrough_typestr = NULL;
1428 static const char *backingstr = NULL;
1429 static const char *background_typestr = NULL;
1430 static const char *stylestr = NULL;
1431 static const char *effect_typestr = NULL;
1432 static const char *shadow_directionstr = NULL;
1433 static const char *tabstopsstr = NULL;
1434 static const char *tab_stopsstr = NULL;
1435 static const char *linesizestr = NULL;
1436 static const char *line_sizestr = NULL;
1437 static const char *linerelsizestr = NULL;
1438 static const char *line_rel_sizestr = NULL;
1439 static const char *linegapstr = NULL;
1440 static const char *line_gapstr = NULL;
1441 static const char *linerelgapstr = NULL;
1442 static const char *line_rel_gapstr = NULL;
1443 static const char *itemstr = NULL;
1444 static const char *linefillstr = NULL;
1445 static const char *line_fillstr = NULL;
1446 static const char *ellipsisstr = NULL;
1447 static const char *passwordstr = NULL;
1448 static const char *replacement_charstr = NULL;
1449 static const char *underline_dash_widthstr = NULL;
1450 static const char *underline_dashed_widthstr = NULL;
1451 static const char *underline_dash_gapstr = NULL;
1452 static const char *underline_dashed_gapstr = NULL;
1453 static const char *underline_heightstr = NULL;
1454 static const char *gfx_filterstr = NULL;
1455 
1456 /**
1457  * @page evas_textblock_style_page Evas Textblock Style Options
1458  *
1459  * @brief This page describes how to style text in an Evas Text Block.
1460  */
1461 
1462 /**
1463  * Internal
1464  * Split str using commas as separators. All characters from the beginning of the string up until
1465  * the first comma (excluded) are copied into part1.
1466  * All characters after the last comma (excluded) up until the end of str are copied into part2.
1467  * Any character in between part1 and part2 is ignored (as right now it's only valid to have 1 comma and 2 strings).
1468  * For example, if str="str1,str2,str3,str4",
1469  * part1 will contain "str1" and part2 will contain "str4".
1470  * part1 and part2 must be already allocated and contain enough space for any possible outcome
1471  * of the parsing. The safest bet is that they should be as big as str.
1472  */
1473 void
_style_string_split(const char * str,char * part1,char * part2)1474 _style_string_split(const char *str, char* part1, char* part2)
1475 {
1476    char *temp = part1;
1477    for (const char *p = str; *p; p++)
1478    {
1479       if (*p == ',')
1480          {
1481             *temp = 0;
1482             temp = part2;
1483             continue;
1484          }
1485       *temp = *p;
1486       temp++;
1487    }
1488    *temp = 0;
1489 }
1490 
1491 #define FORMAT_SHADOW_SET(evas, efl) { \
1492    if (fmt->style != evas) { fmt->style = evas; changed = EINA_TRUE; } \
1493    if (set_default && (_FMT_INFO(effect) != efl)) {_FMT_INFO(effect) = efl; changed = EINA_TRUE;}}
1494 
1495 Eina_Bool
_format_shadow_set(Evas_Object_Textblock_Format * fmt,char * str,Eina_Bool set_default,Efl_Canvas_Textblock_Data * o)1496 _format_shadow_set(Evas_Object_Textblock_Format *fmt, char *str, Eina_Bool set_default, Efl_Canvas_Textblock_Data *o)
1497 {
1498    Eina_Bool changed = EINA_FALSE;
1499 
1500    if (!strcmp(str, "shadow"))
1501      FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_SHADOW, EFL_TEXT_STYLE_EFFECT_TYPE_SHADOW)
1502    else if (!strcmp(str, "outline"))
1503      FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_OUTLINE, EFL_TEXT_STYLE_EFFECT_TYPE_OUTLINE)
1504    else if (!strcmp(str, "soft_outline"))
1505      FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_SOFT_OUTLINE, EFL_TEXT_STYLE_EFFECT_TYPE_SOFT_OUTLINE)
1506    else if (!strcmp(str, "outline_shadow"))
1507      FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_OUTLINE_SHADOW, EFL_TEXT_STYLE_EFFECT_TYPE_OUTLINE_SHADOW)
1508    else if (!strcmp(str, "outline_soft_shadow"))
1509      FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW, EFL_TEXT_STYLE_EFFECT_TYPE_OUTLINE_SOFT_SHADOW)
1510    else if (!strcmp(str, "glow"))
1511      FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_GLOW, EFL_TEXT_STYLE_EFFECT_TYPE_GLOW)
1512    else if (!strcmp(str, "far_shadow"))
1513      FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_FAR_SHADOW, EFL_TEXT_STYLE_EFFECT_TYPE_FAR_SHADOW)
1514    else if (!strcmp(str, "soft_shadow"))
1515      FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_SOFT_SHADOW, EFL_TEXT_STYLE_EFFECT_TYPE_SOFT_SHADOW)
1516    else if (!strcmp(str, "far_soft_shadow"))
1517      FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_FAR_SOFT_SHADOW, EFL_TEXT_STYLE_EFFECT_TYPE_FAR_SOFT_SHADOW)
1518    else   /*off none plain */
1519      FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_PLAIN, EFL_TEXT_STYLE_EFFECT_TYPE_NONE)
1520 
1521    return changed;
1522 }
1523 
1524 #define FORMAT_SHADOW_DIRECTION_SET(direction) { \
1525   unsigned char temp = fmt->style; \
1526   EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_##direction); \
1527   changed = (fmt->style != temp); \
1528   if (set_default && (_FMT_INFO(shadow_direction) != EFL_TEXT_STYLE_SHADOW_DIRECTION_##direction)) \
1529      {_FMT_INFO(shadow_direction) = EFL_TEXT_STYLE_SHADOW_DIRECTION_##direction; changed = EINA_TRUE;}}
1530 
1531 Eina_Bool
_format_shadow_direction_set(Evas_Object_Textblock_Format * fmt,char * str,Eina_Bool set_default,Efl_Canvas_Textblock_Data * o)1532 _format_shadow_direction_set(Evas_Object_Textblock_Format *fmt, char *str, Eina_Bool set_default, Efl_Canvas_Textblock_Data *o)
1533 {
1534    Eina_Bool changed = EINA_FALSE;
1535 
1536    if (!strcmp(str, "bottom_right"))
1537      FORMAT_SHADOW_DIRECTION_SET(BOTTOM_RIGHT)
1538    else if (!strcmp(str, "bottom"))
1539      FORMAT_SHADOW_DIRECTION_SET(BOTTOM)
1540    else if (!strcmp(str, "bottom_left"))
1541      FORMAT_SHADOW_DIRECTION_SET(BOTTOM_LEFT)
1542    else if (!strcmp(str, "left"))
1543      FORMAT_SHADOW_DIRECTION_SET(LEFT)
1544    else if (!strcmp(str, "top_left"))
1545      FORMAT_SHADOW_DIRECTION_SET(TOP_LEFT)
1546    else if (!strcmp(str, "top"))
1547      FORMAT_SHADOW_DIRECTION_SET(TOP)
1548    else if (!strcmp(str, "top_right"))
1549      FORMAT_SHADOW_DIRECTION_SET(TOP_RIGHT)
1550    else if (!strcmp(str, "right"))
1551      FORMAT_SHADOW_DIRECTION_SET(RIGHT)
1552    else
1553      FORMAT_SHADOW_DIRECTION_SET(BOTTOM_RIGHT)
1554 
1555    return changed;
1556 }
1557 
1558 /**
1559  * @internal
1560  * Init the format strings.
1561  */
1562 static void
_format_command_init(void)1563 _format_command_init(void)
1564 {
1565    if (format_refcount == 0)
1566      {
1567         /**
1568          * @page evas_textblock_style_page Evas Textblock Style Options
1569          *
1570          * @section evas_textblock_style_index Index
1571          *
1572          * The following styling commands are accepted:
1573          * @li @ref evas_textblock_style_font
1574          * @li @ref evas_textblock_style_font_fallback
1575          * @li @ref evas_textblock_style_font_size
1576          * @li @ref evas_textblock_style_font_source
1577          * @li @ref evas_textblock_style_font_weight
1578          * @li @ref evas_textblock_style_font_style
1579          * @li @ref evas_textblock_style_font_width
1580          * @li @ref evas_textblock_style_lang
1581          * @li @ref evas_textblock_style_color
1582          * @li @ref evas_textblock_style_underline_color
1583          * @li @ref evas_textblock_style_underline2_color
1584          * @li @ref evas_textblock_style_underline_dash_color
1585          * @li @ref evas_textblock_style_outline_color
1586          * @li @ref evas_textblock_style_shadow_color
1587          * @li @ref evas_textblock_style_glow_color
1588          * @li @ref evas_textblock_style_glow2_color
1589          * @li @ref evas_textblock_style_backing_color
1590          * @li @ref evas_textblock_style_strikethrough_color
1591          * @li @ref evas_textblock_style_align
1592          * @li @ref evas_textblock_style_valign
1593          * @li @ref evas_textblock_style_wrap
1594          * @li @ref evas_textblock_style_left_margin
1595          * @li @ref evas_textblock_style_right_margin
1596          * @li @ref evas_textblock_style_underline
1597          * @li @ref evas_textblock_style_strikethrough
1598          * @li @ref evas_textblock_style_backing
1599          * @li @ref evas_textblock_style_style
1600          * @li @ref evas_textblock_style_tabstops
1601          * @li @ref evas_textblock_style_linesize
1602          * @li @ref evas_textblock_style_linerelsize
1603          * @li @ref evas_textblock_style_linegap
1604          * @li @ref evas_textblock_style_linerelgap
1605          * @li @ref evas_textblock_style_item
1606          * @li @ref evas_textblock_style_linefill
1607          * @li @ref evas_textblock_style_ellipsis
1608          * @li @ref evas_textblock_style_password
1609          * @li @ref evas_textblock_style_underline_dash_width
1610          * @li @ref evas_textblock_style_underline_dash_gap
1611          * @li @ref evas_textblock_style_underline_height
1612          *
1613          * @section evas_textblock_style_contents Contents
1614          */
1615         fontstr = eina_stringshare_add("font");
1616         font_fallbacksstr = eina_stringshare_add("font_fallbacks");
1617         font_sizestr = eina_stringshare_add("font_size");
1618         font_sourcestr = eina_stringshare_add("font_source");
1619         font_weightstr = eina_stringshare_add("font_weight");
1620         font_stylestr = eina_stringshare_add("font_style");
1621         font_widthstr = eina_stringshare_add("font_width");
1622         langstr = eina_stringshare_add("lang");
1623         colorstr = eina_stringshare_add("color");
1624         underline_colorstr = eina_stringshare_add("underline_color");
1625         underline2_colorstr = eina_stringshare_add("underline2_color");
1626         secondary_underline_colorstr = eina_stringshare_add("secondary_underline_color");
1627         underline_dash_colorstr = eina_stringshare_add("underline_dash_color");
1628         underline_dashed_colorstr = eina_stringshare_add("underline_dashed_color");
1629         outline_colorstr = eina_stringshare_add("outline_color");
1630         shadow_colorstr = eina_stringshare_add("shadow_color");
1631         glow_colorstr = eina_stringshare_add("glow_color");
1632         glow2_colorstr = eina_stringshare_add("glow2_color");
1633         secondary_glow_colorstr = eina_stringshare_add("secondary_glow_color");
1634         backing_colorstr = eina_stringshare_add("backing_color");
1635         background_colorstr = eina_stringshare_add("background_color");
1636         strikethrough_colorstr = eina_stringshare_add("strikethrough_color");
1637         alignstr = eina_stringshare_add("align");
1638         valignstr = eina_stringshare_add("valign");
1639         text_valignstr = eina_stringshare_add("text_valign");
1640         wrapstr = eina_stringshare_add("wrap");
1641         left_marginstr = eina_stringshare_add("left_margin");
1642         right_marginstr = eina_stringshare_add("right_margin");
1643         underlinestr = eina_stringshare_add("underline");
1644         underline_typestr = eina_stringshare_add("underline_type");
1645         strikethroughstr = eina_stringshare_add("strikethrough");
1646         strikethrough_typestr = eina_stringshare_add("strikethrough_type");
1647         backingstr = eina_stringshare_add("backing");
1648         background_typestr = eina_stringshare_add("background_type");
1649         stylestr = eina_stringshare_add("style");
1650         effect_typestr = eina_stringshare_add("effect_type");
1651         shadow_directionstr = eina_stringshare_add("shadow_direction");
1652         tabstopsstr = eina_stringshare_add("tabstops");
1653         tab_stopsstr = eina_stringshare_add("tab_stops");
1654         linesizestr = eina_stringshare_add("linesize");
1655         line_sizestr = eina_stringshare_add("line_size");
1656         linerelsizestr = eina_stringshare_add("linerelsize");
1657         line_rel_sizestr = eina_stringshare_add("line_rel_size");
1658         linegapstr = eina_stringshare_add("linegap");
1659         line_gapstr = eina_stringshare_add("line_gap");
1660         linerelgapstr = eina_stringshare_add("linerelgap");
1661         line_rel_gapstr = eina_stringshare_add("line_rel_gap");
1662         itemstr = eina_stringshare_add("item");
1663         linefillstr = eina_stringshare_add("linefill");
1664         line_fillstr = eina_stringshare_add("line_fill");
1665         ellipsisstr = eina_stringshare_add("ellipsis");
1666         passwordstr = eina_stringshare_add("password");
1667         replacement_charstr = eina_stringshare_add("replacement_char");
1668         underline_dash_widthstr = eina_stringshare_add("underline_dash_width");
1669         underline_dashed_widthstr = eina_stringshare_add("underline_dashed_width");
1670         underline_dash_gapstr = eina_stringshare_add("underline_dash_gap");
1671         underline_dashed_gapstr = eina_stringshare_add("underline_dashed_gap");
1672         underline_heightstr = eina_stringshare_add("underline_height");
1673         gfx_filterstr = eina_stringshare_add("gfx_filter"); // FIXME: bg, fg filters
1674      }
1675    format_refcount++;
1676 }
1677 
1678 /**
1679  * @internal
1680  * Shutdown the format strings.
1681  */
1682 static void
_format_command_shutdown(void)1683 _format_command_shutdown(void)
1684 {
1685    if (--format_refcount > 0) return;
1686 
1687    eina_stringshare_del(fontstr);
1688    eina_stringshare_del(font_fallbacksstr);
1689    eina_stringshare_del(font_sizestr);
1690    eina_stringshare_del(font_sourcestr);
1691    eina_stringshare_del(font_weightstr);
1692    eina_stringshare_del(font_stylestr);
1693    eina_stringshare_del(font_widthstr);
1694    eina_stringshare_del(langstr);
1695    eina_stringshare_del(colorstr);
1696    eina_stringshare_del(underline_colorstr);
1697    eina_stringshare_del(underline2_colorstr);
1698    eina_stringshare_del(secondary_underline_colorstr);
1699    eina_stringshare_del(underline_dash_colorstr);
1700    eina_stringshare_del(underline_dashed_colorstr);
1701    eina_stringshare_del(outline_colorstr);
1702    eina_stringshare_del(shadow_colorstr);
1703    eina_stringshare_del(glow_colorstr);
1704    eina_stringshare_del(glow2_colorstr);
1705    eina_stringshare_del(secondary_glow_colorstr);
1706    eina_stringshare_del(backing_colorstr);
1707    eina_stringshare_del(background_colorstr);
1708    eina_stringshare_del(strikethrough_colorstr);
1709    eina_stringshare_del(alignstr);
1710    eina_stringshare_del(valignstr);
1711    eina_stringshare_del(text_valignstr);
1712    eina_stringshare_del(wrapstr);
1713    eina_stringshare_del(left_marginstr);
1714    eina_stringshare_del(right_marginstr);
1715    eina_stringshare_del(underlinestr);
1716    eina_stringshare_del(underline_typestr);
1717    eina_stringshare_del(strikethroughstr);
1718    eina_stringshare_del(strikethrough_typestr);
1719    eina_stringshare_del(backingstr);
1720    eina_stringshare_del(background_typestr);
1721    eina_stringshare_del(stylestr);
1722    eina_stringshare_del(effect_typestr);
1723    eina_stringshare_del(shadow_directionstr);
1724    eina_stringshare_del(tabstopsstr);
1725    eina_stringshare_del(tab_stopsstr);
1726    eina_stringshare_del(linesizestr);
1727    eina_stringshare_del(line_sizestr);
1728    eina_stringshare_del(linerelsizestr);
1729    eina_stringshare_del(line_rel_sizestr);
1730    eina_stringshare_del(linegapstr);
1731    eina_stringshare_del(line_gapstr);
1732    eina_stringshare_del(linerelgapstr);
1733    eina_stringshare_del(line_rel_gapstr);
1734    eina_stringshare_del(itemstr);
1735    eina_stringshare_del(linefillstr);
1736    eina_stringshare_del(line_fillstr);
1737    eina_stringshare_del(ellipsisstr);
1738    eina_stringshare_del(passwordstr);
1739    eina_stringshare_del(replacement_charstr);
1740    eina_stringshare_del(underline_dash_widthstr);
1741    eina_stringshare_del(underline_dashed_widthstr);
1742    eina_stringshare_del(underline_dash_gapstr);
1743    eina_stringshare_del(underline_dashed_gapstr);
1744    eina_stringshare_del(underline_heightstr);
1745    eina_stringshare_del(gfx_filterstr);
1746 }
1747 
1748 /**
1749  * @internal
1750  * Copies str to dst while removing the \\ char, i.e unescape the escape sequences.
1751  *
1752  * @param[out] dst the destination string - Should not be NULL.
1753  * @param[in] src the source string - Should not be NULL.
1754  */
1755 static int
_format_clean_param(char * s)1756 _format_clean_param(char *s)
1757 {
1758    char *ss;
1759    char *ds;
1760    int len = 0;
1761 
1762    ds = s;
1763    for (ss = s; *ss; ss++, ds++, len++)
1764      {
1765         if ((*ss == '\\') && *(ss + 1)) ss++;
1766         if (ds != ss) *ds = *ss;
1767      }
1768    *ds = 0;
1769 
1770    return len;
1771 }
1772 
1773 static void
_format_command_legacy_only(Evas_Object_Textblock_Format * fmt,const char * cmd,char * param,int len)1774 _format_command_legacy_only(Evas_Object_Textblock_Format *fmt, const char *cmd, char *param, int len)
1775 {
1776    if (cmd == backing_colorstr)
1777      /**
1778       * @page evas_textblock_style_page Evas Textblock Style Options
1779       *
1780       * @subsection evas_textblock_style_backing_color Backing Color
1781       *
1782       * Sets a background color for text. The following formats are
1783       * accepted:
1784       * @li "#RRGGBB"
1785       * @li "#RRGGBBAA"
1786       * @li "#RGB"
1787       * @li "#RGBA"
1788       * @li "rgb(r,g,b)"
1789       * @li "rgba(r,g,b,a)"
1790       * @li "color_name"  like "red"
1791       * @code
1792       * backing_color=<color>
1793       * @endcode
1794       */
1795      evas_common_format_color_parse(param, len,
1796            &(fmt->color.backing.r), &(fmt->color.backing.g),
1797            &(fmt->color.backing.b), &(fmt->color.backing.a));
1798    else if (cmd == backingstr)
1799      {
1800         /**
1801          * @page evas_textblock_style_page Evas Textblock Style Options
1802          *
1803          * @subsection evas_textblock_style_backing Backing
1804          *
1805          * Sets if the text will have backing. The value must be one of
1806          * the following:
1807          * @li "off" - No backing
1808          * @li "on" - Backing
1809          * @code
1810          * backing=on/off
1811          * @endcode
1812          */
1813         if (len == 3 && !strcmp(param, "off"))
1814           fmt->backing = 0;
1815         else if (len == 2 && !strcmp(param, "on"))
1816           fmt->backing = 1;
1817      }
1818    else if (cmd == underline2_colorstr)
1819      /**
1820       * @page evas_textblock_style_page Evas Textblock Style Options
1821       *
1822       * @subsection evas_textblock_style_underline2_color Second Underline Color
1823       *
1824       * Sets the color of the second line of underline(when using underline
1825       * mode "double"). The following formats are accepted:
1826       * @li "#RRGGBB"
1827       * @li "#RRGGBBAA"
1828       * @li "#RGB"
1829       * @li "#RGBA"
1830       * @li "rgb(r,g,b)"
1831       * @li "rgba(r,g,b,a)"
1832       * @li "color_name"  like "red"
1833       * @code
1834       * underline2_color=<color>
1835       * @endcode
1836       */
1837      evas_common_format_color_parse(param, len,
1838            &(fmt->color.underline2.r), &(fmt->color.underline2.g),
1839            &(fmt->color.underline2.b), &(fmt->color.underline2.a));
1840    else if (cmd == glow2_colorstr)
1841      /**
1842       * @page evas_textblock_style_page Evas Textblock Style Options
1843       *
1844       * @subsection evas_textblock_style_glow2_color Second Glow Color
1845       *
1846       * Sets the second color of the glow of text. The following formats are
1847       * accepted:
1848       * @li "#RRGGBB"
1849       * @li "#RRGGBBAA"
1850       * @li "#RGB"
1851       * @li "#RGBA"
1852       * @li "rgb(r,g,b)"
1853       * @li "rgba(r,g,b,a)"
1854       * @li "color_name"  like "red"
1855       * @code
1856       * glow2_color=<color>
1857       * @endcode
1858       */
1859      evas_common_format_color_parse(param, len,
1860            &(fmt->color.glow2.r), &(fmt->color.glow2.g),
1861            &(fmt->color.glow2.b), &(fmt->color.glow2.a));
1862    else if (cmd == tabstopsstr)
1863      {
1864         /**
1865          * @page evas_textblock_style_page Evas Textblock Style Options
1866          *
1867          * @subsection evas_textblock_style_tabstops Tabstops
1868          *
1869          * Sets the size of the tab character. The value must be a number
1870          * greater than one.
1871          * @code
1872          * tabstops=<number>
1873          * @endcode
1874          */
1875         fmt->tabstops = atoi(param);
1876         if (fmt->tabstops < 1) fmt->tabstops = 1;
1877      }
1878    else if (cmd == linesizestr)
1879      {
1880         /**
1881          * @page evas_textblock_style_page Evas Textblock Style Options
1882          *
1883          * @subsection evas_textblock_style_linesize Line size
1884          *
1885          * Sets the size of line of text. The value should be a number.
1886          * @warning Setting this value sets linerelsize to 0%!
1887          * @code
1888          * linesize=<number>
1889          * @endcode
1890          */
1891         fmt->linesize = atoi(param);
1892         fmt->linerelsize = 0.0;
1893      }
1894    else if (cmd == linerelsizestr)
1895      {
1896         /**
1897          * @page evas_textblock_style_page Evas Textblock Style Options
1898          *
1899          * @subsection evas_textblock_style_linerelsize Relative line size
1900          *
1901          * Sets the relative size of line of text. The value must be a
1902          * percentage.
1903          * @warning Setting this value sets linesize to 0!
1904          * @code
1905          * linerelsize=<number>%
1906          * @endcode
1907          */
1908         char *endptr = NULL;
1909         double val = strtod(param, &endptr);
1910         if (endptr)
1911           {
1912              while (*endptr && _is_white(*endptr))
1913                endptr++;
1914              if (*endptr == '%')
1915                {
1916                   fmt->linerelsize = val / 100.0;
1917                   fmt->linesize = 0;
1918                   if (fmt->linerelsize < 0.0) fmt->linerelsize = 0.0;
1919                }
1920           }
1921      }
1922    else if (cmd == linegapstr)
1923      {
1924         /**
1925          * @page evas_textblock_style_page Evas Textblock Style Options
1926          *
1927          * @subsection evas_textblock_style_linegap Line gap
1928          *
1929          * Sets the size of the line gap in text. The value should be a
1930          * number.
1931          * @warning Setting this value sets linerelgap to 0%!
1932          * @code
1933          * linegap=<number>
1934          * @endcode
1935          */
1936         fmt->linegap = atoi(param);
1937         fmt->linerelgap = 0.0;
1938      }
1939    else if (cmd == linerelgapstr)
1940      {
1941         /**
1942          * @page evas_textblock_style_page Evas Textblock Style Options
1943          *
1944          * @subsection evas_textblock_style_linerelgap Relative line gap
1945          *
1946          * Sets the relative size of the line gap in text. The value must be
1947          * a percentage.
1948          * @warning Setting this value sets linegap to 0!
1949          * @code
1950          * linerelgap=<number>%
1951          * @endcode
1952          */
1953         char *endptr = NULL;
1954         double val = strtod(param, &endptr);
1955         if (endptr)
1956           {
1957              while (*endptr && _is_white(*endptr))
1958                endptr++;
1959              if (*endptr == '%')
1960                {
1961                   fmt->linerelgap = val / 100.0;
1962                   fmt->linegap = 0;
1963                   if (fmt->linerelgap < 0.0) fmt->linerelgap = 0.0;
1964                }
1965           }
1966      }
1967    else if (cmd == linefillstr)
1968      {
1969         /**
1970          * @page evas_textblock_style_page Evas Textblock Style Options
1971          *
1972          * @subsection evas_textblock_style_linefill Line fill
1973          *
1974          * Sets the size of the line fill in text. The value must be a
1975          * percentage.
1976          * @code
1977          * linefill=<number>%
1978          * @endcode
1979          */
1980         char *endptr = NULL;
1981         double val = strtod(param, &endptr);
1982         if (endptr)
1983           {
1984              while (*endptr && _is_white(*endptr))
1985                endptr++;
1986              if (*endptr == '%')
1987                {
1988                   fmt->linefill = val / 100.0;
1989                   if (fmt->linefill < 0.0) fmt->linefill = 0.0;
1990                }
1991           }
1992      }
1993    else if (cmd == underline_dash_colorstr)
1994      /**
1995       * @page evas_textblock_style_page Evas Textblock Style Options
1996       *
1997       * @subsection evas_textblock_style_underline_dash_color Underline Dash Color
1998       *
1999       * Sets the color of dashed underline. The following formats are accepted:
2000       * @li "#RRGGBB"
2001       * @li "#RRGGBBAA"
2002       * @li "#RGB"
2003       * @li "#RGBA"
2004       * @li "rgb(r,g,b)"
2005       * @li "rgba(r,g,b,a)"
2006       * @li "color_name"  like "red"
2007       * @code
2008       * underline_dash_color=<color>
2009       * @endcode
2010       */
2011      evas_common_format_color_parse(param, len,
2012            &(fmt->color.underline_dash.r), &(fmt->color.underline_dash.g),
2013            &(fmt->color.underline_dash.b), &(fmt->color.underline_dash.a));
2014    else if (cmd == underlinestr)
2015      {
2016         /**
2017          * @page evas_textblock_style_page Evas Textblock Style Options
2018          *
2019          * @subsection evas_textblock_style_underline Underline
2020          *
2021          * Sets if and how a text will be underlined. The value must be one of
2022          * the following:
2023          * @li "off" - No underlining
2024          * @li "single" - A single line under the text
2025          * @li "on" - Alias for "single"
2026          * @li "double" - Two lines under the text
2027          * @li "dashed" - A dashed line under the text
2028          * @code
2029          * underline=off/single/on/double/dashed
2030          * @endcode
2031          */
2032         static const struct {
2033            const char *param;
2034            int len;
2035            Eina_Bool underline;
2036            Eina_Bool underline2;
2037            Eina_Bool underline_dash;
2038         } underlines_named[] = {
2039           { "off", 3, 0, 0, 0 },
2040           { "on", 2, 1, 0, 0 },
2041           { "single", 6, 1, 0, 0 },
2042           { "double", 6, 1, 1, 0 },
2043           { "dashed", 6, 0, 0, 1 },
2044           { NULL, 0, 0, 0, 0 }
2045         };
2046         unsigned int i;
2047 
2048         fmt->underline = fmt->underline2 = fmt->underline_dash = 0;
2049         for (i = 0; underlines_named[i].param; ++i)
2050           if (underlines_named[i].len == len &&
2051               !strcmp(underlines_named[i].param, param))
2052             {
2053                fmt->underline = underlines_named[i].underline;
2054                fmt->underline2 = underlines_named[i].underline2;
2055                fmt->underline_dash = underlines_named[i].underline_dash;
2056                break;
2057             }
2058      }
2059    else if (cmd == strikethroughstr)
2060      {
2061         /**
2062          * @page evas_textblock_style_page Evas Textblock Style Options
2063          *
2064          * @subsection evas_textblock_style_strikethrough Strikethrough
2065          *
2066          * Sets if the text will be striked through. The value must be one of
2067          * the following:
2068          * @li "off" - No strikethrough
2069          * @li "on" - Strikethrough
2070          * @code
2071          * strikethrough=on/off
2072          * @endcode
2073          */
2074         if (len == 3 && !strcmp(param, "off"))
2075           fmt->strikethrough = 0;
2076         else if (len == 2 && !strcmp(param, "on"))
2077           fmt->strikethrough = 1;
2078      }
2079    else if (cmd == stylestr)
2080      {
2081         /**
2082          * @page evas_textblock_style_page Evas Textblock Style Options
2083          *
2084          * @subsection evas_textblock_style_style Style
2085          *
2086          * Sets the style of the text. The value must be a string composed of
2087          * two comma separated parts. The first part of the value sets the
2088          * appearance of the text, the second the position.
2089          *
2090          * The first part may be any of the following values:
2091          * @li "plain"
2092          * @li "off" - Alias for "plain"
2093          * @li "none" - Alias for "plain"
2094          * @li "shadow"
2095          * @li "outline"
2096          * @li "soft_outline"
2097          * @li "outline_shadow"
2098          * @li "outline_soft_shadow"
2099          * @li "glow"
2100          * @li "far_shadow"
2101          * @li "soft_shadow"
2102          * @li "far_soft_shadow"
2103          * The second part may be any of the following values:
2104          * @li "bottom_right"
2105          * @li "bottom"
2106          * @li "bottom_left"
2107          * @li "left"
2108          * @li "top_left"
2109          * @li "top"
2110          * @li "top_right"
2111          * @li "right"
2112          * @code
2113          * style=<appearance>,<position>
2114          * @endcode
2115          */
2116         char *part1, *part2;
2117 
2118         part1 = alloca(len + 1);
2119         *part1 = 0;
2120 
2121         part2 = alloca(len + 1);
2122         *part2 = 0;
2123 
2124         _style_string_split(param, part1, part2);
2125 
2126         _format_shadow_set(fmt, part1, EINA_FALSE, NULL);
2127 
2128         if (*part2)
2129           _format_shadow_direction_set(fmt, part2, EINA_FALSE, NULL);
2130      }
2131    else if (cmd == underline_dash_widthstr)
2132      {
2133         /**
2134          * @page evas_textblock_style_page Evas Textblock Style Options
2135          *
2136          * @subsection evas_textblock_style_underline_dash_width Underline dash width
2137          *
2138          * Sets the width of the underline dash. The value should be a number.
2139          * @code
2140          * underline_dash_width=<number>
2141          * @endcode
2142          */
2143         fmt->underline_dash_width = atoi(param);
2144         if (fmt->underline_dash_width <= 0) fmt->underline_dash_width = 1;
2145      }
2146    else if (cmd == underline_dash_gapstr)
2147      {
2148         /**
2149          * @page evas_textblock_style_page Evas Textblock Style Options
2150          *
2151          * @subsection evas_textblock_style_underline_dash_gap Underline dash gap
2152          *
2153          * Sets the gap of the underline dash. The value should be a number.
2154          * @code
2155          * underline_dash_gap=<number>
2156          * @endcode
2157          */
2158         fmt->underline_dash_gap = atoi(param);
2159         if (fmt->underline_dash_gap <= 0) fmt->underline_dash_gap = 1;
2160      }
2161 }
2162 
2163 static void
_format_command_unified_only(Efl_Canvas_Textblock_Data * o,Evas_Object_Textblock_Format * fmt,const char * cmd,char * param,int len)2164 _format_command_unified_only( Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Format *fmt, const char *cmd, char *param, int len)
2165 {
2166    if (cmd == background_colorstr)
2167      evas_common_format_color_parse(param, len,
2168            &(fmt->color.backing.r), &(fmt->color.backing.g),
2169            &(fmt->color.backing.b), &(fmt->color.backing.a));
2170    else if (cmd == background_typestr)
2171      {
2172         if (len == 4 && !strcmp(param, "none"))
2173           fmt->backing = 0;
2174         else if (len == 5 && !strcmp(param, "solid"))
2175           fmt->backing = 1;
2176      }
2177    else if (cmd == secondary_underline_colorstr)
2178      evas_common_format_color_parse(param, len,
2179            &(fmt->color.underline2.r), &(fmt->color.underline2.g),
2180            &(fmt->color.underline2.b), &(fmt->color.underline2.a));
2181    else if (cmd == secondary_glow_colorstr)
2182      evas_common_format_color_parse(param, len,
2183            &(fmt->color.glow2.r), &(fmt->color.glow2.g),
2184            &(fmt->color.glow2.b), &(fmt->color.glow2.a));
2185    else if (cmd == tab_stopsstr)
2186      {
2187         fmt->tabstops = atoi(param);
2188         if (fmt->tabstops < 1) fmt->tabstops = 1;
2189      }
2190    else if (cmd == line_sizestr)
2191      {
2192         fmt->linesize = atoi(param);
2193         fmt->linerelsize = 0.0;
2194      }
2195    else if (cmd == line_rel_sizestr)
2196      {
2197         char *endptr = NULL;
2198         double val = strtod(param, &endptr);
2199         if (endptr)
2200           {
2201              while (*endptr && _is_white(*endptr))
2202                endptr++;
2203              if (*endptr == '%')
2204                {
2205                   fmt->linerelsize = val / 100.0;
2206                   fmt->linesize = 0;
2207                   if (fmt->linerelsize < 0.0) fmt->linerelsize = 0.0;
2208                }
2209           }
2210      }
2211    else if (cmd == line_gapstr)
2212      {
2213         fmt->linegap = atoi(param);
2214         fmt->linerelgap = 0.0;
2215      }
2216    else if (cmd == line_rel_gapstr)
2217      {
2218         char *endptr = NULL;
2219         double val = strtod(param, &endptr);
2220         if (endptr)
2221           {
2222              while (*endptr && _is_white(*endptr))
2223                endptr++;
2224              if (*endptr == '%')
2225                {
2226                   fmt->linerelgap = val / 100.0;
2227                   fmt->linegap = 0;
2228                   if (fmt->linerelgap < 0.0) fmt->linerelgap = 0.0;
2229                }
2230           }
2231      }
2232    else if (cmd == line_fillstr)
2233      {
2234         char *endptr = NULL;
2235         double val = strtod(param, &endptr);
2236         if (endptr)
2237           {
2238              while (*endptr && _is_white(*endptr))
2239                endptr++;
2240              if (*endptr == '%')
2241                {
2242                   fmt->linefill = val / 100.0;
2243                   if (fmt->linefill < 0.0) fmt->linefill = 0.0;
2244                }
2245           }
2246      }
2247    else if (cmd == underline_dashed_colorstr)
2248      evas_common_format_color_parse(param, len,
2249            &(fmt->color.underline_dash.r), &(fmt->color.underline_dash.g),
2250            &(fmt->color.underline_dash.b), &(fmt->color.underline_dash.a));
2251    else if (cmd == underline_typestr)
2252      {
2253         typedef struct {
2254              const char *param;
2255              int len;
2256              Eina_Bool underline;
2257              Eina_Bool underline2;
2258              Eina_Bool underline_dash;
2259         } Underline_Info;
2260 
2261         fmt->underline = fmt->underline2 = fmt->underline_dash = 0;
2262         unsigned int i;
2263         #define SET_UNDERLINE_VALUES() \
2264         for (i = 0; underlines_named[i].param; ++i) { \
2265           if (underlines_named[i].len == len && \
2266               !strcmp(underlines_named[i].param, param)) \
2267             { \
2268                fmt->underline = underlines_named[i].underline; \
2269                fmt->underline2 = underlines_named[i].underline2; \
2270                fmt->underline_dash = underlines_named[i].underline_dash; \
2271                break;\
2272             } \
2273         }
2274         static Underline_Info underlines_named[] = {
2275                     { "none", 4, 0, 0, 0 },
2276                     { "single", 6, 1, 0, 0 },
2277                     { "double", 6, 1, 1, 0 },
2278                     { "dashed", 6, 0, 0, 1 },
2279                     { NULL, 0, 0, 0, 0 }
2280              };
2281         SET_UNDERLINE_VALUES();
2282      }
2283    else if (cmd == strikethrough_typestr)
2284      {
2285         if (len == 4 && !strcmp(param, "none"))
2286           fmt->strikethrough = 0;
2287         else if (len == 6 && !strcmp(param, "single"))
2288           fmt->strikethrough = 1;
2289      }
2290    else if (cmd == effect_typestr)
2291      {
2292         _format_shadow_set(fmt, param, EINA_TRUE, o);
2293      }
2294    else if (cmd == shadow_directionstr)
2295      {
2296         _format_shadow_direction_set(fmt, param, EINA_TRUE, o);
2297      }
2298    else if (cmd == underline_dashed_widthstr)
2299      {
2300         fmt->underline_dash_width = atoi(param);
2301         if (fmt->underline_dash_width <= 0) fmt->underline_dash_width = 1;
2302      }
2303    else if (cmd == underline_dashed_gapstr)
2304      {
2305         fmt->underline_dash_gap = atoi(param);
2306         if (fmt->underline_dash_gap <= 0) fmt->underline_dash_gap = 1;
2307      }
2308 }
2309 
2310 /**
2311  * @internal
2312  * Parses the cmd and parameter and adds the parsed format to fmt.
2313  *
2314  * @param obj the evas object - should not be NULL.
2315  * @param fmt The format to populate - should not be NULL.
2316  * @param[in] cmd the command to process, should be stringshared.
2317  * @param[in] param the parameter of the command. may modify the string.
2318  */
2319 static void
_format_command(Evas_Object * eo_obj,Evas_Object_Textblock_Format * fmt,const char * cmd,char * param)2320 _format_command(Evas_Object *eo_obj, Evas_Object_Textblock_Format *fmt, const char *cmd, char *param)
2321 {
2322    int len;
2323    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
2324    Eina_Bool is_legacy = o->is_legacy;
2325 
2326    len = _format_clean_param(param);
2327 
2328    /* If we are changing the font, create the fdesc. */
2329    if ((cmd == font_weightstr) || (cmd == font_widthstr) ||
2330          (cmd == font_stylestr) || (cmd == langstr) ||
2331          (cmd == fontstr) || (cmd == font_fallbacksstr))
2332      {
2333         if (!fmt->font.fdesc)
2334           {
2335              fmt->font.fdesc = evas_font_desc_new();
2336 
2337              /* Set default language according to locale. */
2338              eina_stringshare_replace(&(fmt->font.fdesc->lang),
2339                                       evas_font_lang_normalize("auto"));
2340           }
2341         else if (!fmt->font.fdesc->is_new)
2342           {
2343              Evas_Font_Description *old = fmt->font.fdesc;
2344              fmt->font.fdesc = evas_font_desc_dup(fmt->font.fdesc);
2345              if (old) evas_font_desc_unref(old);
2346           }
2347      }
2348 
2349 
2350    if (cmd == fontstr)
2351      {
2352         /**
2353          * @page evas_textblock_style_page Evas Textblock Style Options
2354          *
2355          * @subsection evas_textblock_style_font Font
2356          *
2357          * This sets the name of the font to be used.
2358          * @code
2359          * font=<font name>
2360          * @endcode
2361          */
2362         evas_font_name_parse(fmt->font.fdesc, param);
2363      }
2364    else if (cmd == font_fallbacksstr)
2365      {
2366         /**
2367          * @page evas_textblock_style_page Evas Textblock Style Options
2368          *
2369          * @subsection evas_textblock_style_font_fallback Font fallback
2370          *
2371          * This sets the name of the fallback font to be used. This font will
2372          * be used if the primary font is not available.
2373          * @code
2374          * font_fallbacks=<font name>
2375          * @endcode
2376          */
2377         eina_stringshare_replace(&(fmt->font.fdesc->fallbacks), param);
2378      }
2379    else if (cmd == font_sizestr)
2380      {
2381         /**
2382          * @page evas_textblock_style_page Evas Textblock Style Options
2383          *
2384          * @subsection evas_textblock_style_font_size Font size
2385          *
2386          * This sets the the size of font in points to be used.
2387          * @code
2388          * font_size=<size>
2389          * @endcode
2390          */
2391         int v;
2392 
2393         v = atoi(param);
2394         if (v != fmt->font.size)
2395           {
2396              fmt->font.size = v;
2397           }
2398      }
2399    else if (cmd == font_sourcestr)
2400      {
2401         /**
2402          * @page evas_textblock_style_page Evas Textblock Style Options
2403          *
2404          * @subsection evas_textblock_style_font_source Font source
2405          *
2406          * Specify an object from which to search for the font.
2407          * @code
2408          * font_source=<source>
2409          * @endcode
2410          */
2411         if ((!fmt->font.source) ||
2412               ((fmt->font.source) && (strcmp(fmt->font.source, param))))
2413           {
2414              eina_stringshare_replace(&(fmt->font.source), param);
2415           }
2416      }
2417    else if (cmd == font_weightstr)
2418      {
2419         /**
2420          * @page evas_textblock_style_page Evas Textblock Style Options
2421          *
2422          * @subsection evas_textblock_style_font_weight Font weight
2423          *
2424          * Sets the weight of the font. The value must be one of:
2425          * @li "normal"
2426          * @li "thin"
2427          * @li "ultralight"
2428          * @li "extralight"
2429          * @li "light"
2430          * @li "book"
2431          * @li "medium"
2432          * @li "semibold"
2433          * @li "bold"
2434          * @li "ultrabold"
2435          * @li "extrabold"
2436          * @li "black"
2437          * @li "extrablack"
2438          * @code
2439          * font_weight=<weight>
2440          * @endcode
2441          */
2442         fmt->font.fdesc->weight = evas_font_style_find(param,
2443                                                        param + len,
2444                                                        EVAS_FONT_STYLE_WEIGHT);
2445      }
2446    else if (cmd == font_stylestr)
2447      {
2448         /**
2449          * @page evas_textblock_style_page Evas Textblock Style Options
2450          *
2451          * @subsection evas_textblock_style_font_style Font style
2452          *
2453          * Sets the style of the font. The value must be one of:
2454          * @li "normal"
2455          * @li "oblique"
2456          * @li "italic"
2457          * @code
2458          * font_style=<style>
2459          * @endcode
2460          */
2461         fmt->font.fdesc->slant = evas_font_style_find(param,
2462                                                       param + len,
2463                                                       EVAS_FONT_STYLE_SLANT);
2464      }
2465    else if (cmd == font_widthstr)
2466      {
2467         /**
2468          * @page evas_textblock_style_page Evas Textblock Style Options
2469          *
2470          * @subsection evas_textblock_style_font_width Font width
2471          *
2472          * Sets the width of the font. The value must be one of:
2473          * @li "normal"
2474          * @li "ultracondensed"
2475          * @li "extracondensed"
2476          * @li "condensed"
2477          * @li "semicondensed"
2478          * @li "semiexpanded"
2479          * @li "expanded"
2480          * @li "extraexpanded"
2481          * @li "ultraexpanded"
2482          * @code
2483          * font_width=<width>
2484          * @endcode
2485          */
2486         fmt->font.fdesc->width = evas_font_style_find(param,
2487                                                       param + len,
2488                                                       EVAS_FONT_STYLE_WIDTH);
2489      }
2490    else if (cmd == langstr)
2491      {
2492         /**
2493          * @page evas_textblock_style_page Evas Textblock Style Options
2494          *
2495          * @subsection evas_textblock_style_lang Language
2496          *
2497          * Sets the language of the text for FontConfig.
2498          * The value can either be a language text or one of presets:
2499          * @li "auto" - Respects system locale settings as language
2500          * @li "none" - Disable language support
2501          * @code
2502          * lang=<language>
2503          * @endcode
2504          */
2505         eina_stringshare_replace(&(fmt->font.fdesc->lang),
2506                                  evas_font_lang_normalize(param));
2507      }
2508    else if (cmd == colorstr)
2509      /**
2510       * @page evas_textblock_style_page Evas Textblock Style Options
2511       *
2512       * @subsection evas_textblock_style_color Color
2513       *
2514       * Sets the color of the text. The following formats are accepted:
2515       * @li "#RRGGBB"
2516       * @li "#RRGGBBAA"
2517       * @li "#RGB"
2518       * @li "#RGBA"
2519       * @li "rgb(r,g,b)"
2520       * @li "rgba(r,g,b,a)"
2521       * @li "color_name"  like "red"
2522       * @code
2523       * color=<color>
2524       * @endcode
2525       */
2526      evas_common_format_color_parse(param, len,
2527            &(fmt->color.normal.r), &(fmt->color.normal.g),
2528            &(fmt->color.normal.b), &(fmt->color.normal.a));
2529    else if (cmd == underline_colorstr)
2530      /**
2531       * @page evas_textblock_style_page Evas Textblock Style Options
2532       *
2533       * @subsection evas_textblock_style_underline_color Underline Color
2534       *
2535       * Sets the color of the underline. The following formats are accepted:
2536       * @li "#RRGGBB"
2537       * @li "#RRGGBBAA"
2538       * @li "#RGB"
2539       * @li "#RGBA"
2540       * @li "rgb(r,g,b)"
2541       * @li "rgba(r,g,b,a)"
2542       * @li "color_name"  like "red"
2543       * @code
2544       * underline_color=<color>
2545       * @endcode
2546       */
2547      evas_common_format_color_parse(param, len,
2548            &(fmt->color.underline.r), &(fmt->color.underline.g),
2549            &(fmt->color.underline.b), &(fmt->color.underline.a));
2550    else if (cmd == outline_colorstr)
2551      /**
2552       * @page evas_textblock_style_page Evas Textblock Style Options
2553       *
2554       * @subsection evas_textblock_style_outline_color Outline Color
2555       *
2556       * Sets the color of the outline of the text. The following formats are
2557       * accepted:
2558       * @li "#RRGGBB"
2559       * @li "#RRGGBBAA"
2560       * @li "#RGB"
2561       * @li "#RGBA"
2562       * @li "rgb(r,g,b)"
2563       * @li "rgba(r,g,b,a)"
2564       * @li "color_name"  like "red"
2565       * @code
2566       * outline_color=<color>
2567       * @endcode
2568       */
2569      evas_common_format_color_parse(param, len,
2570            &(fmt->color.outline.r), &(fmt->color.outline.g),
2571            &(fmt->color.outline.b), &(fmt->color.outline.a));
2572    else if (cmd == shadow_colorstr)
2573      /**
2574       * @page evas_textblock_style_page Evas Textblock Style Options
2575       *
2576       * @subsection evas_textblock_style_shadow_color Shadow Color
2577       *
2578       * Sets the color of the shadow of the text. The following formats are
2579       * accepted:
2580       * @li "#RRGGBB"
2581       * @li "#RRGGBBAA"
2582       * @li "#RGB"
2583       * @li "#RGBA"
2584       * @li "rgb(r,g,b)"
2585       * @li "rgba(r,g,b,a)"
2586       * @li "color_name"  like "red"
2587       * @code
2588       * shadow_color=<color>
2589       * @endcode
2590       */
2591      evas_common_format_color_parse(param, len,
2592            &(fmt->color.shadow.r), &(fmt->color.shadow.g),
2593            &(fmt->color.shadow.b), &(fmt->color.shadow.a));
2594    else if (cmd == glow_colorstr)
2595      /**
2596       * @page evas_textblock_style_page Evas Textblock Style Options
2597       *
2598       * @subsection evas_textblock_style_glow_color First Glow Color
2599       *
2600       * Sets the first color of the glow of text. The following formats are
2601       * accepted:
2602       * @li "#RRGGBB"
2603       * @li "#RRGGBBAA"
2604       * @li "#RGB"
2605       * @li "#RGBA"
2606       * @li "rgb(r,g,b)"
2607       * @li "rgba(r,g,b,a)"
2608       * @li "color_name"  like "red"
2609       * @code
2610       * glow_color=<color>
2611       * @endcode
2612       */
2613      evas_common_format_color_parse(param, len,
2614            &(fmt->color.glow.r), &(fmt->color.glow.g),
2615            &(fmt->color.glow.b), &(fmt->color.glow.a));
2616    else if (cmd == strikethrough_colorstr)
2617      /**
2618       * @page evas_textblock_style_page Evas Textblock Style Options
2619       *
2620       * @subsection evas_textblock_style_strikethrough_color Strikethrough Color
2621       *
2622       * Sets the color of text that is striked through. The following formats
2623       * are accepted:
2624       * @li "#RRGGBB"
2625       * @li "#RRGGBBAA"
2626       * @li "#RGB"
2627       * @li "#RGBA"
2628       * @li "rgb(r,g,b)"
2629       * @li "rgba(r,g,b,a)"
2630       * @li "color_name"  like "red"
2631       * @code
2632       * strikethrough_color=<color>
2633       * @endcode
2634       */
2635      evas_common_format_color_parse(param, len,
2636            &(fmt->color.strikethrough.r), &(fmt->color.strikethrough.g),
2637            &(fmt->color.strikethrough.b), &(fmt->color.strikethrough.a));
2638    else if (cmd == alignstr)
2639      {
2640         /**
2641          * @page evas_textblock_style_page Evas Textblock Style Options
2642          *
2643          * @subsection evas_textblock_style_align Horizontal Align
2644          *
2645          * Sets the horizontal alignment of the text. The value can either be
2646          * a number, a percentage or one of several presets:
2647          * @li "auto" - Respects LTR/RTL settings
2648          * @li "locale" - Respects locale(language) direction settings
2649          * @li "center" - Centers the text in the line
2650          * @li "middle" - Alias for "center"
2651          * @li "left" - Puts the text at the left of the line
2652          * @li "right" - Puts the text at the right of the line
2653          * @li "start" - Respects LTR/RTL settings. It is same with "auto"
2654          * @li "end" - Puts the text at the opposite side of LTR/RTL settings
2655          * @li \<number\> - A number between 0.0 and 1.0 where 0.0 represents
2656          * "left" and 1.0 represents "right"
2657          * @li \<number\>% - A percentage between 0% and 100% where 0%
2658          * represents "left" and 100% represents "right"
2659          * @code
2660          * align=<value or preset>
2661          * @endcode
2662          */
2663         if ((len == 4 && !strcmp(param, "auto")) ||
2664             (len == 5 && !strcmp(param, "start")))
2665           {
2666              fmt->halign_auto = EVAS_TEXTBLOCK_ALIGN_AUTO_NORMAL;
2667           }
2668         else if (len == 3 && !strcmp(param, "end"))
2669           {
2670              fmt->halign_auto = EVAS_TEXTBLOCK_ALIGN_AUTO_END;
2671           }
2672         else if (len == 6 && !strcmp(param, "locale"))
2673           {
2674              fmt->halign_auto = EVAS_TEXTBLOCK_ALIGN_AUTO_LOCALE;
2675           }
2676         else
2677           {
2678              static const struct {
2679                 const char *param;
2680                 int len;
2681                 double halign;
2682              } halign_named[] = {
2683                { "middle", 6, 0.5 },
2684                { "center", 6, 0.5 },
2685                { "left", 4, 0.0 },
2686                { "right", 5, 1.0 },
2687                { NULL, 0, 0.0 }
2688              };
2689              unsigned int i;
2690 
2691              for (i = 0; halign_named[i].param; i++)
2692                if (len == halign_named[i].len &&
2693                    !strcmp(param, halign_named[i].param))
2694                  {
2695                     fmt->halign = halign_named[i].halign;
2696                     break;
2697                  }
2698 
2699              if (halign_named[i].param == NULL)
2700                {
2701                   char *endptr = NULL;
2702                   double val = strtod(param, &endptr);
2703                   if (endptr)
2704                     {
2705                        while (*endptr && _is_white(*endptr))
2706                          endptr++;
2707                        if (*endptr == '%')
2708                          val /= 100.0;
2709                     }
2710                   fmt->halign = val;
2711                   if (fmt->halign < 0.0) fmt->halign = 0.0;
2712                   else if (fmt->halign > 1.0) fmt->halign = 1.0;
2713                }
2714              fmt->halign_auto = EVAS_TEXTBLOCK_ALIGN_AUTO_NONE;
2715           }
2716      }
2717    else if (cmd == valignstr)
2718      {
2719         /**
2720          * @page evas_textblock_style_page Evas Textblock Style Options
2721          *
2722          * @subsection evas_textblock_style_valign Vertical Align
2723          *
2724          * Sets the vertical alignment of the text. The value can either be
2725          * a number or one of the following presets:
2726          * @li "top" - Puts text at the top of the line
2727          * @li "center" - Centers the text in the line
2728          * @li "middle" - Alias for "center"
2729          * @li "bottom" - Puts the text at the bottom of the line
2730          * @li "baseline" - Baseline
2731          * @li "base" - Alias for "baseline"
2732          * @li \<number\> - A number between 0.0 and 1.0 where 0.0 represents
2733          * "top" and 1.0 represents "bottom"
2734          * @li \<number\>% - A percentage between 0% and 100% where 0%
2735          * represents "top" and 100% represents "bottom"
2736          * @code
2737          * valign=<value or preset>
2738          * @endcode
2739          *
2740          * See explanation of baseline at:
2741          * https://en.wikipedia.org/wiki/Baseline_%28typography%29
2742          */
2743         static const struct {
2744            const char *param;
2745            int len;
2746            double valign;
2747         } valign_named[] = {
2748           { "top", 3, 0.0 },
2749           { "middle", 6, 0.5 },
2750           { "center", 6, 0.5 },
2751           { "bottom", 6, 1.0 },
2752           { "baseline", 8, -1.0 },
2753           { "base", 4, -1.0 },
2754           { NULL, 0, 0 }
2755         };
2756         unsigned int i;
2757 
2758         for (i = 0; valign_named[i].param; i++)
2759           if (len == valign_named[i].len &&
2760               !strcmp(valign_named[i].param, param))
2761             {
2762                fmt->valign = valign_named[i].valign;
2763                break;
2764             }
2765 
2766         if (valign_named[i].param == NULL)
2767           {
2768              char *endptr = NULL;
2769              double val = strtod(param, &endptr);
2770              if (endptr)
2771                {
2772                   while (*endptr && _is_white(*endptr))
2773                     endptr++;
2774                   if (*endptr == '%')
2775                     val /= 100.0;
2776                }
2777              fmt->valign = val;
2778              if (fmt->valign < 0.0) fmt->valign = 0.0;
2779              else if (fmt->valign > 1.0) fmt->valign = 1.0;
2780           }
2781      }
2782    else if (cmd == text_valignstr)
2783      {
2784         Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
2785         char *endptr = NULL;
2786         double val = strtod(param, &endptr);
2787         o->valign = val;
2788         if (o->valign < 0.0) o->valign = 0.0;
2789         else if (o->valign > 1.0) o->valign = 1.0;
2790      }
2791    else if (cmd == wrapstr)
2792      {
2793         /**
2794          * @page evas_textblock_style_page Evas Textblock Style Options
2795          *
2796          * @subsection evas_textblock_style_wrap Wrap
2797          *
2798          * Sets the wrap policy of the text. The value must be one of the
2799          * following:
2800          * @li "word" - Only wraps lines at word boundaries
2801          * @li "char" - Wraps at any character
2802          * @li "mixed" - Wrap at words if possible, if not at any character
2803          * @li "hyphenation" - Hyphenate if possible, if not wrap at words if possible, if not at any character
2804          * @li "" - Don't wrap
2805          * @code
2806          * wrap=<value or preset>
2807          * @endcode
2808          */
2809         static const struct {
2810            const char *param;
2811            int len;
2812            Eina_Bool wrap_word;
2813            Eina_Bool wrap_char;
2814            Eina_Bool wrap_mixed;
2815            Eina_Bool wrap_hyphenation;
2816         } wrap_named[] = {
2817           { "word",        4,  1, 0, 0, 0 },
2818           { "char",        4,  0, 1, 0, 0 },
2819           { "mixed",       5,  0, 0, 1, 0 },
2820           { "hyphenation", 11, 0, 0, 0, 1 },
2821           { NULL,          0,  0, 0, 0, 0 }
2822         };
2823         unsigned int i;
2824 
2825         fmt->wrap_word = fmt->wrap_mixed = fmt->wrap_char =
2826            fmt->wrap_hyphenation = 0;
2827         for (i = 0; wrap_named[i].param; i++)
2828           if (wrap_named[i].len == len &&
2829               !strcmp(wrap_named[i].param, param))
2830             {
2831                fmt->wrap_word = wrap_named[i].wrap_word;
2832                fmt->wrap_char = wrap_named[i].wrap_char;
2833                fmt->wrap_mixed = wrap_named[i].wrap_mixed;
2834                fmt->wrap_hyphenation = wrap_named[i].wrap_hyphenation;
2835                break;
2836             }
2837 
2838 #ifdef HAVE_HYPHEN
2839         /* Hyphenating textblocks are registered as "clients", so we load/unload
2840          * the hyphenation dictionaries on-demand. */
2841         if (fmt->wrap_hyphenation)
2842           {
2843              _dicts_hyphen_init(eo_obj);
2844           }
2845 #endif
2846 
2847      }
2848    else if (cmd == left_marginstr)
2849      {
2850         /**
2851          * @page evas_textblock_style_page Evas Textblock Style Options
2852          *
2853          * @subsection evas_textblock_style_left_margin Left margin
2854          *
2855          * Sets the left margin of the text. The value can be a number, an
2856          * increment, decrement or "reset":
2857          * @li +\<number\> - Increments existing left margin by \<number\>
2858          * @li -\<number\> - Decrements existing left margin by \<number\>
2859          * @li \<number\> - Sets left margin to \<number\>
2860          * @li "reset" - Sets left margin to 0
2861          * @code
2862          * left_margin=<value or reset>
2863          * @endcode
2864          */
2865         if (len == 5 && !strcmp(param, "reset"))
2866           fmt->margin.l = 0;
2867         else
2868           {
2869              if (param[0] == '+')
2870                fmt->margin.l += atoi(&(param[1]));
2871              else if (param[0] == '-')
2872                fmt->margin.l -= atoi(&(param[1]));
2873              else
2874                fmt->margin.l = atoi(param);
2875              if (fmt->margin.l < 0) fmt->margin.l = 0;
2876           }
2877      }
2878    else if (cmd == right_marginstr)
2879      {
2880         /**
2881          * @page evas_textblock_style_page Evas Textblock Style Options
2882          *
2883          * @subsection evas_textblock_style_right_margin Right margin
2884          *
2885          * Sets the right margin of the text. The value can be a number, an
2886          * increment, decrement or "reset":
2887          * @li +\<number\> - Increments existing right margin by \<number\>
2888          * @li -\<number\> - Decrements existing right margin by \<number\>
2889          * @li \<number\> - Sets left margin to \<number\>
2890          * @li "reset" - Sets left margin to 0
2891          * @code
2892          * right_margin=<value or reset>
2893          * @endcode
2894          */
2895         if (len == 5 && !strcmp(param, "reset"))
2896           fmt->margin.r = 0;
2897         else
2898           {
2899              if (param[0] == '+')
2900                fmt->margin.r += atoi(&(param[1]));
2901              else if (param[0] == '-')
2902                fmt->margin.r -= atoi(&(param[1]));
2903              else
2904                fmt->margin.r = atoi(param);
2905              if (fmt->margin.r < 0) fmt->margin.r = 0;
2906           }
2907      }
2908    else if (cmd == itemstr)
2909      {
2910         /**
2911          * @page evas_textblock_style_page Evas Textblock Style Options
2912          *
2913          * @subsection evas_textblock_style_item Item
2914          *
2915          * Not implemented! Does nothing!
2916          * @code
2917          * item=<anything>
2918          * @endcode
2919          */
2920         // itemstr == replacement object items in textblock - inline imges
2921         // for example
2922      }
2923    else if (cmd == ellipsisstr)
2924      {
2925         /**
2926          * @page evas_textblock_style_page Evas Textblock Style Options
2927          *
2928          * @subsection evas_textblock_style_ellipsis Ellipsis
2929          *
2930          * Sets ellipsis mode. The value should be a number. Any value smaller
2931          * than 0.0 or greater than 1.0 disables ellipsis.
2932          * A value of 0 means ellipsizing the leftmost portion of the text
2933          * first, 1 on the other hand the rightmost portion.
2934          * @code
2935          * ellipsis=<number>
2936          * @endcode
2937          */
2938         char *endptr = NULL;
2939         fmt->ellipsis = strtod(param, &endptr);
2940         if ((fmt->ellipsis < 0.0) || (fmt->ellipsis > 1.0))
2941           fmt->ellipsis = -1.0;
2942         else
2943           {
2944              Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
2945              o->have_ellipsis = 1;
2946           }
2947      }
2948    else if (cmd == passwordstr)
2949      {
2950         /**
2951          * @page evas_textblock_style_page Evas Textblock Style Options
2952          *
2953          * @subsection evas_textblock_style_password Password
2954          *
2955          * Sets if the text is being used for passwords. Enabling this causes
2956          * all characters to be substituted for '*'.
2957          * Value must be one of the following:
2958          * @li "on" - Enable
2959          * @li "off" - Disable
2960          * @code
2961          * password=<number>
2962          * @endcode
2963          */
2964         if (len == 3 && !strcmp(param, "off"))
2965           fmt->password = 0;
2966         else if (len == 2 && !strcmp(param, "on"))
2967           fmt->password = 1;
2968      }
2969    else if (cmd == replacement_charstr)
2970      {
2971         Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
2972         eina_stringshare_replace(&o->repch, param);
2973      }
2974    else if (cmd == underline_heightstr)
2975      {
2976         /**
2977          * @page evas_textblock_style_page Evas Textblock Style Options
2978          *
2979          * @subsection evas_textblock_style_underline_height Underline height
2980          *
2981          * Sets the height of the single underline. The value should be a floating number.
2982          * @code
2983          * underline_height=<floatingnumber>
2984          * @endcode
2985          */
2986         fmt->underline_height = atof(param);
2987         if (fmt->underline_height <= 0.0) fmt->underline_height = 1.0;
2988      }
2989    else if (cmd == gfx_filterstr)
2990      {
2991         /**
2992          * @page evas_textblock_style_page Evas Textblock Style Options
2993          *
2994          * @subsection evas_textblock_style_gfx_filter Gfx Filter
2995          *
2996          * Experimental filter, see efl_gfx_filter for more information.
2997          * @code
2998          * gfx_filter='filter name'
2999          * @endcode
3000          */
3001         if (!fmt->gfx_filter)
3002           fmt->gfx_filter = calloc(1, sizeof(Efl_Canvas_Textblock_Filter));
3003         eina_stringshare_replace(&fmt->gfx_filter->name, param);
3004      }
3005    else
3006      {
3007         if (is_legacy)
3008           {
3009              _format_command_legacy_only(fmt, cmd, param, len);
3010           }
3011         else
3012           {
3013              _format_command_unified_only(o, fmt, cmd, param, len);
3014           }
3015 
3016      }
3017 }
3018 
3019 //FIXME Create common function for both _default _format_command & _format_command
3020 /**
3021  * @internal
3022  * Parses the cmd and parameter and adds the parsed format to fmt and fmt info.
3023  *
3024  * @param obj the evas object - should not be NULL.
3025  * @param fmt The format to populate - should not be NULL.
3026  * @param[in] cmd the command to process, should be stringshared.
3027  * @param[in] param the parameter of the command. may modify the string.
3028  */
3029 static Eina_Bool
_default_format_command(Evas_Object * eo_obj,Evas_Object_Textblock_Format * fmt,const char * cmd,char * param)3030 _default_format_command(Evas_Object *eo_obj, Evas_Object_Textblock_Format *fmt, const char *cmd, char *param)
3031 {
3032    EINA_SAFETY_ON_NULL_RETURN_VAL(param, EINA_FALSE);
3033 
3034    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
3035    int len = _format_clean_param(param);
3036    Eina_Bool changed = EINA_FALSE;
3037    const char * font_prefix = "font_";
3038 
3039    ASYNC_BLOCK;
3040 
3041    if (cmd == fontstr)
3042      {
3043         char *end = strchr(param, ':');
3044 
3045         if (!end)
3046         changed = eina_stringshare_replace(&(_FMT_INFO(font)), param);
3047         else
3048         changed = eina_stringshare_replace_length(&(_FMT_INFO(font)), param, end - param);
3049 
3050         while (end)
3051         {
3052            const char *tend;
3053            param = end;
3054            end = strchr(end + 1, ':');
3055            if (!end)
3056               tend = param + strlen(param);
3057            else
3058               tend = end;
3059 
3060            char *equal = strchr(param, '=');
3061            if (equal)
3062               {
3063                  char font_param[strlen(font_prefix) + (equal - param)];
3064                  font_param[0] = '\0';
3065                  strcat(font_param, font_prefix);
3066                  strncat(font_param, &param[1], equal - param -1);
3067                  const char *key = eina_stringshare_add(font_param);
3068 
3069                  char val[tend - equal];
3070                  val[0] = '\0';
3071                  strncat(val, &equal[1], tend - equal -1);
3072 
3073                  _default_format_command(eo_obj, fmt, key, val);
3074                  eina_stringshare_del(key);
3075               }
3076         }
3077      }
3078    else if (cmd == font_fallbacksstr)
3079      {
3080         changed = eina_stringshare_replace(&(_FMT_INFO(font_fallbacks)), param);
3081      }
3082    else if (cmd == font_sizestr)
3083      {
3084         int size;
3085 
3086         size = atoi(param);
3087         if (size > 0 && _FMT_INFO(size) != size)
3088           {
3089              _FMT_INFO(size) = size;
3090              changed = EINA_TRUE;
3091           }
3092      }
3093    else if (cmd == font_sourcestr)
3094      {
3095         Eina_Stringshare *font_source = _FMT_INFO(font_source);
3096         if ((!font_source) ||
3097               ((font_source) && (strcmp(font_source, param))))
3098           {
3099              changed = eina_stringshare_replace(&(_FMT_INFO(font_source)), param);
3100           }
3101      }
3102    else if (cmd == font_weightstr)
3103      {
3104         unsigned int weight = evas_font_style_find(param,
3105                                                    param + len,
3106                                                    EVAS_FONT_STYLE_WEIGHT);
3107 
3108         if (_FMT_INFO(font_weight) != weight)
3109           {
3110              _FMT_INFO(font_weight) = weight;
3111              changed = EINA_TRUE;
3112           }
3113      }
3114    else if (cmd == font_stylestr)
3115      {
3116         unsigned int slant = evas_font_style_find(param,
3117                                                   param + len,
3118                                                   EVAS_FONT_STYLE_SLANT);
3119 
3120         if (_FMT_INFO(font_slant) != slant)
3121           {
3122              _FMT_INFO(font_slant) = slant;
3123              changed = EINA_TRUE;
3124           }
3125      }
3126    else if (cmd == font_widthstr)
3127      {
3128         unsigned int width = evas_font_style_find(param,
3129                                                   param + len,
3130                                                   EVAS_FONT_STYLE_WIDTH);
3131 
3132         if (_FMT_INFO(font_width) != width)
3133           {
3134              _FMT_INFO(font_width) = width;
3135              changed = EINA_TRUE;
3136           }
3137      }
3138    else if (cmd == langstr)
3139      {
3140         changed = eina_stringshare_replace(&(_FMT_INFO(font_lang)),
3141                                            param);
3142      }
3143    else if (cmd == gfx_filterstr)
3144      {
3145         if (!fmt->gfx_filter)
3146           fmt->gfx_filter = calloc(1, sizeof(Efl_Canvas_Textblock_Filter));
3147         eina_stringshare_replace(&fmt->gfx_filter->name, param);
3148         eina_stringshare_replace(&(_FMT_INFO(gfx_filter_name)), param);
3149      }
3150    else if (cmd == wrapstr)
3151      {
3152         Efl_Text_Format_Wrap wrap = _FMT_INFO(wrap);
3153 
3154         if (!strcmp("word", param))
3155           wrap = EFL_TEXT_FORMAT_WRAP_WORD;
3156         else if (!strcmp("char", param))
3157           wrap = EFL_TEXT_FORMAT_WRAP_CHAR;
3158         else if (!strcmp("mixed", param))
3159           wrap = EFL_TEXT_FORMAT_WRAP_MIXED;
3160         else if (!strcmp("hyphenation", param))
3161           wrap = EFL_TEXT_FORMAT_WRAP_HYPHENATION;
3162         else if (!strcmp("none", param))
3163           wrap = EFL_TEXT_FORMAT_WRAP_NONE;
3164 
3165         if (_FMT_INFO(wrap) != wrap)
3166           {
3167              _FMT_INFO(wrap) = wrap;
3168              _FMT(wrap_word) = (wrap == EFL_TEXT_FORMAT_WRAP_WORD);
3169              _FMT(wrap_char) = (wrap == EFL_TEXT_FORMAT_WRAP_CHAR);
3170              _FMT(wrap_mixed) = (wrap == EFL_TEXT_FORMAT_WRAP_MIXED);
3171              _FMT(wrap_hyphenation) = (wrap == EFL_TEXT_FORMAT_WRAP_HYPHENATION);
3172              changed = EINA_TRUE;
3173           }
3174      }
3175    else if (cmd == stylestr)
3176      {
3177         char *part1, *part2;
3178 
3179         part1 = alloca(len + 1);
3180         *part1 = 0;
3181 
3182         part2 = alloca(len + 1);
3183         *part2 = 0;
3184 
3185         _style_string_split(param, part1, part2);
3186         changed = _format_shadow_set(fmt, part1, EINA_TRUE, o);
3187 
3188         if (*part2)
3189           changed = _format_shadow_direction_set(fmt, part2, EINA_TRUE, o) || changed;
3190      }
3191    else
3192      {
3193         /* Update reset of formats like color, ...etc*/
3194         _format_command(eo_obj, fmt, cmd, param);
3195         /*FIXME update only when change happened*/
3196         changed = EINA_TRUE;
3197      }
3198 
3199   return changed;
3200 }
3201 
3202 /*
3203  * @internal
3204  * just to create a constant without using marco
3205  * 2 cacheline is enough for the string we parse
3206  * usually color or color_class strings.
3207  * reduce it if  this number is too high.
3208  */
3209 enum _Internal{ ALLOCATOR_SIZE = 120 };
3210 
3211 /*
3212  * @internal
3213  * A simple stack allocator which first
3214  * tries to create a string in the stack (buffer
3215  * it holds). if the string size is bigger than its
3216  * buffer capacity it will fall back to creating the
3217  * string on heap.
3218  * USAGE :
3219  * Allocator a;
3220  * _allocator_init(&a);
3221  * _allocator_make_string(&a, "hello world", 11);
3222  * ... //use the string
3223  * ._allocator_reset(&a);
3224  * Always call _allocator_reset() after
3225  * you done with the string.
3226  */
3227 typedef struct _Allocator
3228 {
3229    char       stack[ALLOCATOR_SIZE];
3230    char      *heap;
3231    size_t     heap_size;
3232 } Allocator;
3233 
3234 static inline void
_allocator_init(Allocator * allocator)3235 _allocator_init(Allocator* allocator)
3236 {
3237    if (allocator)
3238      {
3239         allocator->heap = NULL;
3240         allocator->heap_size = 0;
3241      }
3242 }
3243 
3244 static inline void
_allocator_reset(Allocator * allocator)3245 _allocator_reset(Allocator* allocator)
3246 {
3247    if (allocator && allocator->heap)
3248      {
3249         free(allocator->heap);
3250         allocator->heap = NULL;
3251         allocator->heap_size = 0;
3252      }
3253 }
3254 
3255 static char *
_allocator_make_string(Allocator * allocator,const char * str,size_t size)3256 _allocator_make_string(Allocator* allocator, const char* str, size_t size)
3257 {
3258    if (!allocator) return NULL;
3259    if (!size) return NULL;
3260 
3261    if (size + 1 < ALLOCATOR_SIZE)
3262      {
3263         memcpy(allocator->stack, str, size);
3264         allocator->stack[size] = '\0';
3265         return allocator->stack;
3266      }
3267    //fallback to heap
3268    if (allocator->heap && allocator->heap_size < (size + 1))
3269      {
3270         free(allocator->heap);
3271         allocator->heap = NULL;
3272         allocator->heap_size = 0;
3273      }
3274 
3275    if (!allocator->heap)
3276      {
3277         allocator->heap = malloc(size + 1);
3278         allocator->heap_size = (size + 1);
3279      }
3280 
3281    if (!allocator->heap)
3282      {
3283         allocator->heap_size = 0;
3284         return NULL;
3285      }
3286 
3287    memcpy(allocator->heap, str, size);
3288    allocator->heap[size] = '\0';
3289    return allocator->heap;
3290 }
3291 
3292 /**
3293  * @internal
3294  * Returns @c EINA_TRUE if the item is a format parameter, @c EINA_FALSE
3295  * otherwise.
3296  *
3297  * @param[in] item the item to check - Not NULL.
3298  */
3299 static Eina_Bool
_format_is_param(const char * item)3300 _format_is_param(const char *item)
3301 {
3302    if (strchr(item, '=')) return EINA_TRUE;
3303    return EINA_FALSE;
3304 }
3305 
3306 /**
3307  * @internal
3308  * Returns first occurrence of whitespace character
3309  * otherwise return NULL.
3310  *
3311  * @param[in] string to search.
3312  */
3313 static const char*
_strchr_whitespace(const char * str)3314 _strchr_whitespace(const char* str)
3315 {
3316    if (!str) return NULL;
3317    while (*str && !isspace(*str)) str++;
3318    if(*str) return str;
3319    return NULL;
3320 }
3321 
3322 /**
3323  * @internal
3324  * Parse the format item and populate key and val with the stringshares that
3325  * correspond to the formats parsed.
3326  * It expects item to be of the structure:
3327  * "key=val"
3328  *
3329  * @param[in] item the item to parse - Not NULL.
3330  * @param[out] key where to store the key at - Not NULL.
3331  * @param[out] val where to store the value at - Not NULL.
3332  */
3333 static Eina_Bool
_format_param_parse(const char * item,const char ** key,char ** val,Allocator * allocator)3334 _format_param_parse(const char *item, const char **key, char **val, Allocator *allocator)
3335 {
3336    const char *start, *end;
3337    char *tmp, *s, *d;
3338    size_t len;
3339 
3340    start = strchr(item, '=');
3341    if (!start) return EINA_FALSE;
3342    *key = eina_stringshare_add_length(item, start - item);
3343    start++; /* Advance after the '=' */
3344    /* If we can find a quote as the first non-space char,
3345     * our new delimiter is a quote, not a space. */
3346    while (isspace(*start))
3347       start++;
3348 
3349    if (*start == '\'')
3350      {
3351         start++;
3352         end = strchr(start, '\'');
3353         while ((end) && (end > start) && (end[-1] == '\\'))
3354           end = strchr(end + 1, '\'');
3355      }
3356    else
3357      {
3358         end = _strchr_whitespace(start);
3359         while ((end) && (end > start) && (end[-1] == '\\'))
3360           end = _strchr_whitespace(end + 1);
3361      }
3362 
3363    /* Null terminate before the spaces */
3364    if (end) len = end - start;
3365    else len = strlen(start);
3366 
3367    tmp = _allocator_make_string(allocator, start, len);
3368    if (!tmp) goto end;
3369 
3370    for (d = tmp, s = tmp; *s; s++)
3371      {
3372         if (*s != '\\')
3373           {
3374              *d = *s;
3375              d++;
3376           }
3377      }
3378    *d = '\0';
3379 
3380 end:
3381    *val = tmp;
3382 
3383    return EINA_TRUE;
3384 }
3385 
3386 /**
3387  * @internal
3388  * This function parses the format passed in *s and advances s to point to the
3389  * next format item, while returning the current one as the return value.
3390  * @param s The current and returned position in the format string.
3391  * @return the current item parsed from the string.
3392  */
3393 static const char *
_format_parse(const char ** s,Eina_Bool all_whitespaces)3394 _format_parse(const char **s, Eina_Bool all_whitespaces)
3395 {
3396    const char *p;
3397    const char *s1 = NULL, *s2 = NULL;
3398    Eina_Bool quote = EINA_FALSE;
3399 
3400    p = *s;
3401    if (*p == 0) return NULL;
3402    for (;;)
3403      {
3404         if (!s1)
3405           {
3406              if (all_whitespaces)
3407                {
3408                   if (!isspace(*p))
3409                     s1 = p;
3410                }
3411              else if(*p != ' ')
3412                s1 = p;
3413              if (*p == 0) break;
3414           }
3415         else if (!s2)
3416           {
3417              if (*p == '\'')
3418                {
3419                   quote = !quote;
3420                }
3421 
3422              if ((p > *s) && (p[-1] != '\\') && (!quote))
3423                {
3424                   if (all_whitespaces)
3425                   {
3426                      if (isspace(*p))
3427                        s2 = p;
3428                   }
3429                   else if(*p == ' ')
3430                     s2 = p;
3431                }
3432              if (*p == 0) s2 = p;
3433           }
3434         p++;
3435         if (s1 && s2)
3436           {
3437              *s = s2;
3438              return s1;
3439           }
3440      }
3441    *s = p;
3442    return NULL;
3443 }
3444 
3445 /**
3446  * @internal
3447  * Parse the format str and populate fmt with the formats found.
3448  *
3449  * @param obj The evas object - Not NULL.
3450  * @param[out] fmt The format to populate - Not NULL.
3451  * @param[in] str the string to parse.- Not NULL.
3452  * @param[in] is_default_format if this is default format true else false.
3453  */
3454 static void
_format_fill(Evas_Object * eo_obj,Evas_Object_Textblock_Format * fmt,const char * str,Eina_Bool is_default_format)3455 _format_fill(Evas_Object *eo_obj, Evas_Object_Textblock_Format *fmt, const char *str, Eina_Bool is_default_format)
3456 {
3457    const char *s;
3458    const char *item;
3459    Eina_Bool changed = EINA_FALSE;
3460 
3461    if (!str) return;
3462 
3463    s = str;
3464 
3465    /* get rid of any spaces at the start of the string */
3466    while (isspace(*s)) s++;
3467 
3468    Allocator allocator;
3469    _allocator_init(&allocator);
3470 
3471    while ((item = _format_parse(&s, EINA_TRUE)))
3472      {
3473         const char *key = NULL;
3474         char *val = NULL;
3475         if (_format_param_parse(item, &key, &val, &allocator))
3476           {
3477              if ((key) && (val))
3478                {
3479                   if (is_default_format == EINA_TRUE)
3480                     {
3481                        if (_default_format_command(eo_obj, fmt, key, val))
3482                          changed = EINA_TRUE;
3483                     }
3484                   else
3485                     _format_command(eo_obj, fmt, key, val);
3486                }
3487              eina_stringshare_del(key);
3488           }
3489         else
3490           {
3491              /* immediate - not handled here */
3492           }
3493      }
3494 
3495    if (is_default_format == EINA_TRUE && changed)
3496      {
3497         Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
3498         _canvas_text_format_changed(eo_obj, o);
3499      }
3500 
3501    _allocator_reset(&allocator);
3502 }
3503 
3504 #define PRINTF_APPEND_STR(name, val) eina_strbuf_append_printf(format_buffer, "%s=%s ", name, val)
3505 #define PRINTF_APPEND_INT(name, val) eina_strbuf_append_printf(format_buffer, "%s=%i ", name, val)
3506 #define PRINTF_APPEND_COLOR(name, r, g, b, a) eina_strbuf_append_printf(format_buffer, "%s=rgba(%i,%i,%i,%i) ", name, r, g, b, a)
3507 #define PRINTF_APPEND_FLOAT(name, val) eina_strbuf_append_printf(format_buffer, "%s=%f ", name, val)
3508 #define PRINTF_APPEND_PERCENT_FLOAT(name, val) eina_strbuf_append_printf(format_buffer, "%s=%f%% ", name, val)
3509 
3510 /**
3511  * @internal
3512  * get all formats in one string
3513  *
3514  * @param obj The evas object - Not NULL.
3515  * @param[out] fmt The format to convert to string - Not NULL.
3516  * @return the format as string, must be freed by the user.
3517  */
3518 static char *
_format_string_get(const Eo * eo_obj,Evas_Object_Textblock_Format * fmt)3519 _format_string_get(const Eo *eo_obj, Evas_Object_Textblock_Format *fmt)
3520 {
3521    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
3522    Eina_Strbuf *format_buffer = eina_strbuf_new();
3523 
3524    //format info
3525 
3526    if (_FMT_INFO(font))
3527      PRINTF_APPEND_STR(fontstr, _FMT_INFO(font));
3528 
3529    if (_FMT_INFO(font_fallbacks))
3530      PRINTF_APPEND_STR(font_fallbacksstr, _FMT_INFO(font_fallbacks));
3531 
3532    PRINTF_APPEND_INT(font_sizestr, _FMT_INFO(size));
3533 
3534    if (_FMT_INFO(font_source))
3535      PRINTF_APPEND_STR(font_sourcestr, _FMT_INFO(font_source));
3536 
3537    const char *weight_value_str = evas_font_style_find_str(_FMT_INFO(font_weight), EVAS_FONT_STYLE_WEIGHT);
3538    if (!weight_value_str)
3539      weight_value_str = "normal";
3540 
3541    PRINTF_APPEND_STR(font_weightstr, weight_value_str);
3542 
3543    const char *slant_value_str = evas_font_style_find_str(_FMT_INFO(font_slant), EVAS_FONT_STYLE_SLANT);
3544    if (!slant_value_str)
3545      slant_value_str = "normal";
3546 
3547    PRINTF_APPEND_STR(font_stylestr, slant_value_str);
3548 
3549    const char *width_value_str = evas_font_style_find_str(_FMT_INFO(font_width), EVAS_FONT_STYLE_WIDTH);
3550    if (!width_value_str)
3551      width_value_str = "normal";
3552 
3553    PRINTF_APPEND_STR(font_widthstr, width_value_str);
3554 
3555    if (_FMT_INFO(font_lang))
3556      PRINTF_APPEND_STR(langstr, _FMT_INFO(font_lang));
3557 
3558    if (_FMT_INFO(gfx_filter_name))
3559      PRINTF_APPEND_STR(gfx_filterstr, _FMT_INFO(gfx_filter_name));
3560 
3561    char *wrap_value_str;
3562    Efl_Text_Format_Wrap wrap = _FMT_INFO(wrap);
3563 
3564    switch (wrap)
3565      {
3566         case EFL_TEXT_FORMAT_WRAP_CHAR:
3567           wrap_value_str = "char";
3568           break;
3569         case EFL_TEXT_FORMAT_WRAP_MIXED:
3570           wrap_value_str = "mixed";
3571           break;
3572         case EFL_TEXT_FORMAT_WRAP_HYPHENATION:
3573           wrap_value_str = "hyphenation";
3574           break;
3575         case EFL_TEXT_FORMAT_WRAP_WORD:
3576           wrap_value_str = "word";
3577           break;
3578         default:
3579           wrap_value_str = "none";
3580           break;
3581      }
3582 
3583    PRINTF_APPEND_STR(wrapstr, wrap_value_str);
3584 
3585    //format
3586    PRINTF_APPEND_COLOR(colorstr, fmt->color.normal.r, fmt->color.normal.g,
3587                              fmt->color.normal.b, fmt->color.normal.a);
3588 
3589    PRINTF_APPEND_COLOR(underline_colorstr, fmt->color.underline.r, fmt->color.underline.g,
3590                              fmt->color.underline.b, fmt->color.underline.a);
3591 
3592    PRINTF_APPEND_COLOR(secondary_underline_colorstr, fmt->color.underline2.r, fmt->color.underline2.g,
3593                              fmt->color.underline2.b, fmt->color.underline2.a);
3594 
3595    PRINTF_APPEND_COLOR(underline_dashed_colorstr, fmt->color.underline_dash.r, fmt->color.underline_dash.g,
3596                              fmt->color.underline_dash.b, fmt->color.underline_dash.a);
3597 
3598    PRINTF_APPEND_COLOR(outline_colorstr, fmt->color.outline.r, fmt->color.outline.g,
3599                              fmt->color.outline.b, fmt->color.outline.a);
3600 
3601    PRINTF_APPEND_COLOR(shadow_colorstr, fmt->color.shadow.r, fmt->color.shadow.g,
3602                              fmt->color.shadow.b, fmt->color.shadow.a);
3603 
3604    PRINTF_APPEND_COLOR(glow_colorstr, fmt->color.glow.r, fmt->color.glow.g,
3605                              fmt->color.glow.b, fmt->color.glow.a);
3606 
3607    PRINTF_APPEND_COLOR(secondary_glow_colorstr, fmt->color.glow2.r, fmt->color.glow2.g,
3608                              fmt->color.glow2.b, fmt->color.glow2.a);
3609 
3610    PRINTF_APPEND_COLOR(background_colorstr, fmt->color.backing.r, fmt->color.backing.g,
3611                              fmt->color.backing.b, fmt->color.backing.a);
3612 
3613    PRINTF_APPEND_COLOR(strikethrough_colorstr, fmt->color.strikethrough.r, fmt->color.strikethrough.g,
3614                              fmt->color.strikethrough.b, fmt->color.strikethrough.a);
3615 
3616    char *halign_value_str = NULL;
3617    Evas_Textblock_Align_Auto halign = fmt->halign_auto;
3618 
3619    switch (halign)
3620      {
3621         case EVAS_TEXTBLOCK_ALIGN_AUTO_NORMAL:
3622           halign_value_str = "auto";
3623           break;
3624         case EVAS_TEXTBLOCK_ALIGN_AUTO_END:
3625           halign_value_str = "end";
3626           break;
3627         case EVAS_TEXTBLOCK_ALIGN_AUTO_LOCALE:
3628           halign_value_str = "locale";
3629           break;
3630         case EVAS_TEXTBLOCK_ALIGN_AUTO_NONE:
3631           if (EINA_DBL_EQ(fmt->halign, 0.5))
3632             halign_value_str = "center";
3633           else if (EINA_DBL_EQ(fmt->halign, 0.0))
3634             halign_value_str = "left";
3635           else if (EINA_DBL_EQ(fmt->halign, 1.0))
3636             halign_value_str = "right";
3637 
3638           break;
3639      }
3640 
3641    if (halign_value_str != NULL)
3642      PRINTF_APPEND_STR(alignstr, halign_value_str);
3643    else
3644      PRINTF_APPEND_FLOAT(alignstr, fmt->halign);
3645 
3646 
3647    char *valign_value_str = NULL;
3648 
3649    if (EINA_DBL_EQ(fmt->valign, 0.5))
3650      valign_value_str = "center";
3651    else if (EINA_DBL_EQ(fmt->valign, 0.0))
3652      valign_value_str = "top";
3653    else if (EINA_DBL_EQ(fmt->valign, 1.0))
3654      valign_value_str = "bottom";
3655    else if (EINA_DBL_EQ(fmt->valign, -1.0))
3656      valign_value_str = "baseline";
3657 
3658    if (valign_value_str != NULL)
3659      PRINTF_APPEND_STR(valignstr, valign_value_str);
3660    else
3661      PRINTF_APPEND_FLOAT(valignstr, fmt->valign);
3662 
3663    PRINTF_APPEND_FLOAT(text_valignstr, o->valign);
3664    PRINTF_APPEND_INT(left_marginstr, fmt->margin.l);
3665    PRINTF_APPEND_INT(right_marginstr, fmt->margin.r);
3666 
3667 
3668    char *underline_value_str = "none";
3669 
3670    if (fmt->underline == EINA_TRUE && fmt->underline2 == EINA_TRUE)
3671      underline_value_str = "double";
3672    else if (fmt->underline == EINA_TRUE)
3673      underline_value_str = "single";
3674    else if (fmt->underline_dash == EINA_TRUE)
3675      underline_value_str = "dashed";
3676 
3677    PRINTF_APPEND_STR(underline_typestr, underline_value_str);
3678    PRINTF_APPEND_STR(strikethrough_typestr, (fmt->strikethrough == 0 ? "none" : "single"));
3679    PRINTF_APPEND_STR(background_typestr, (fmt->backing == 0 ? "none" : "solid"));
3680 
3681    Efl_Text_Style_Effect_Type effect = _FMT_INFO(effect);
3682 
3683    switch (effect)
3684      {
3685         case EFL_TEXT_STYLE_EFFECT_TYPE_NONE:
3686           PRINTF_APPEND_STR(effect_typestr, "none");
3687           break;
3688         case EFL_TEXT_STYLE_EFFECT_TYPE_SHADOW:
3689           PRINTF_APPEND_STR(effect_typestr, "shadow");
3690           break;
3691         case EFL_TEXT_STYLE_EFFECT_TYPE_OUTLINE:
3692           PRINTF_APPEND_STR(effect_typestr, "outline");
3693           break;
3694         case EFL_TEXT_STYLE_EFFECT_TYPE_SOFT_OUTLINE:
3695           PRINTF_APPEND_STR(effect_typestr, "soft_outline");
3696           break;
3697         case EFL_TEXT_STYLE_EFFECT_TYPE_OUTLINE_SHADOW:
3698           PRINTF_APPEND_STR(effect_typestr, "outline_shadow");
3699           break;
3700         case EFL_TEXT_STYLE_EFFECT_TYPE_OUTLINE_SOFT_SHADOW:
3701           PRINTF_APPEND_STR(effect_typestr, "outline_soft_shadow");
3702           break;
3703         case EFL_TEXT_STYLE_EFFECT_TYPE_GLOW:
3704           PRINTF_APPEND_STR(effect_typestr, "glow");
3705           break;
3706         case EFL_TEXT_STYLE_EFFECT_TYPE_FAR_SHADOW:
3707           PRINTF_APPEND_STR(effect_typestr, "far_shadow");
3708           break;
3709         case EFL_TEXT_STYLE_EFFECT_TYPE_SOFT_SHADOW:
3710           PRINTF_APPEND_STR(effect_typestr, "soft_shadow");
3711           break;
3712         case EFL_TEXT_STYLE_EFFECT_TYPE_FAR_SOFT_SHADOW:
3713           PRINTF_APPEND_STR(effect_typestr, "far_soft_shadow");
3714           break;
3715         default:
3716           break;
3717      }
3718 
3719    Efl_Text_Style_Shadow_Direction shadow_direction = _FMT_INFO(shadow_direction);
3720 
3721    switch (shadow_direction)
3722      {
3723         case EFL_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT:
3724           PRINTF_APPEND_STR(shadow_directionstr, "bottom_right");
3725           break;
3726         case EFL_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM:
3727           PRINTF_APPEND_STR(shadow_directionstr, "bottom");
3728           break;
3729         case EFL_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT:
3730           PRINTF_APPEND_STR(shadow_directionstr, "bottom_left");
3731           break;
3732         case EFL_TEXT_STYLE_SHADOW_DIRECTION_LEFT:
3733           PRINTF_APPEND_STR(shadow_directionstr, "left");
3734           break;
3735         case EFL_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT:
3736           PRINTF_APPEND_STR(shadow_directionstr, "top_left");
3737           break;
3738         case EFL_TEXT_STYLE_SHADOW_DIRECTION_TOP:
3739           PRINTF_APPEND_STR(shadow_directionstr, "top");
3740           break;
3741         case EFL_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT:
3742           PRINTF_APPEND_STR(shadow_directionstr, "top_right");
3743           break;
3744         case EFL_TEXT_STYLE_SHADOW_DIRECTION_RIGHT:
3745           PRINTF_APPEND_STR(shadow_directionstr, "right");
3746           break;
3747         default:
3748           break;
3749      }
3750 
3751    PRINTF_APPEND_INT(tab_stopsstr, fmt->tabstops);
3752    PRINTF_APPEND_INT(line_sizestr, fmt->linesize);
3753    PRINTF_APPEND_PERCENT_FLOAT(line_rel_sizestr, (fmt->linerelsize*100));
3754    PRINTF_APPEND_INT(line_gapstr, fmt->linegap);
3755    PRINTF_APPEND_PERCENT_FLOAT(line_rel_gapstr, (fmt->linerelgap*100));
3756    PRINTF_APPEND_PERCENT_FLOAT(line_fillstr, (fmt->linefill*100));
3757    PRINTF_APPEND_FLOAT(ellipsisstr, fmt->ellipsis);
3758    PRINTF_APPEND_STR(passwordstr, (fmt->password == 0 ? "off" : "on"));
3759 
3760    if (o->repch)
3761      PRINTF_APPEND_STR(replacement_charstr, o->repch);
3762 
3763    PRINTF_APPEND_INT(underline_dashed_widthstr, fmt->underline_dash_width);
3764    PRINTF_APPEND_INT(underline_dashed_gapstr, fmt->underline_dash_gap);
3765    PRINTF_APPEND_FLOAT(underline_heightstr, fmt->underline_height);
3766 
3767    const char *temp = eina_strbuf_string_get(format_buffer);
3768    size_t len = strlen(temp);
3769    char *format_str = malloc(len+1);
3770    strcpy(format_str, temp);
3771    eina_strbuf_free(format_buffer);
3772 
3773    return format_str;
3774 }
3775 
3776 /**
3777  * @internal
3778  * Duplicate a format and return the duplicate.
3779  *
3780  * @param obj The evas object - Not NULL.
3781  * @param[in] fmt The format to duplicate - Not NULL.
3782  * @return the copy of the format.
3783  */
3784 static Evas_Object_Textblock_Format *
_format_dup(Evas_Object * eo_obj,const Evas_Object_Textblock_Format * fmt)3785 _format_dup(Evas_Object *eo_obj, const Evas_Object_Textblock_Format *fmt)
3786 {
3787    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
3788    Evas_Object_Textblock_Format *fmt2;
3789 
3790    fmt2 = calloc(1, sizeof(Evas_Object_Textblock_Format));
3791    memcpy(fmt2, fmt, sizeof(Evas_Object_Textblock_Format));
3792    fmt2->ref = 1;
3793    if (fmt->font.fdesc) fmt2->font.fdesc = evas_font_desc_ref(fmt->font.fdesc);
3794 
3795    if (fmt->font.source) fmt2->font.source = eina_stringshare_add(fmt->font.source);
3796 
3797    /* FIXME: just ref the font here... */
3798    fmt2->font.font = evas_font_load(obj->layer->evas->font_path,
3799                                     obj->layer->evas->hinting,
3800                                     fmt2->font.fdesc,
3801                                     fmt2->font.source,
3802                                     (int)(((double) fmt2->font.size) * obj->cur->scale),
3803                                     fmt2->font.bitmap_scalable);
3804 
3805    if (fmt->gfx_filter)
3806      {
3807         fmt2->gfx_filter = malloc(sizeof(*fmt2->gfx_filter));
3808         memcpy(fmt2->gfx_filter, fmt->gfx_filter, sizeof(*fmt->gfx_filter));
3809         fmt2->gfx_filter->name = eina_stringshare_ref(fmt->gfx_filter->name);
3810         if (fmt->gfx_filter->dc)
3811           fmt2->gfx_filter->dc = ENFN->context_dup(ENC, fmt->gfx_filter->dc);
3812      }
3813 
3814    return fmt2;
3815 }
3816 
3817 
3818 
3819 
3820 typedef enum
3821 {
3822    TEXTBLOCK_POSITION_START,
3823    TEXTBLOCK_POSITION_END,
3824    TEXTBLOCK_POSITION_ELSE,
3825    TEXTBLOCK_POSITION_SINGLE
3826 } Textblock_Position;
3827 
3828 /**
3829  * @internal
3830  * @typedef Ctxt
3831  *
3832  * A pack of information that needed to be passed around in the layout engine,
3833  * packed for easier access.
3834  */
3835 typedef struct _Ctxt Ctxt;
3836 
3837 struct _Ctxt
3838 {
3839    Evas_Object *obj;
3840    Efl_Canvas_Textblock_Data *o;
3841    Evas_Object_Protected_Data *evas_o;
3842    Evas_Public_Data *evas;
3843 
3844    Evas_Object_Textblock_Paragraph *paragraphs;
3845    Evas_Object_Textblock_Paragraph *par;
3846    Evas_Object_Textblock_Line *ln;
3847    Evas_Object_Textblock_Text_Item *hyphen_ti;
3848 
3849 
3850    Eina_List *format_stack;
3851    Evas_Object_Textblock_Format *fmt;
3852 
3853    Eina_List *obs_infos; /**< Extra information for items in current line. */
3854    Eina_List *ellip_prev_it; /* item that is placed before ellipsis item (0.0 <= ellipsis < 1.0), if required */
3855 
3856    int x, y;
3857    int w, h;
3858    int wmax, hmax;
3859    int ascent, descent;
3860    int maxascent, maxdescent;
3861    int marginl, marginr;
3862    int line_no;
3863    int underline_extend;
3864    int have_underline, have_underline2;
3865    double align, valign;
3866    struct {
3867         int                              l, r, t, b;
3868    } style_pad;
3869    Textblock_Position position;
3870    Evas_Textblock_Align_Auto align_auto : 2;
3871    Eina_Bool width_changed : 1;
3872    Eina_Bool handle_obstacles : 1;
3873    Eina_Bool vertical_ellipsis : 1;  /**<EINA_TRUE if needs vertical ellipsis, else EINA_FALSE. */
3874 };
3875 
3876 static void _layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Text_Item *ti, Eina_List *rel);
3877 static void _text_item_update_sizes(Ctxt *c, Evas_Object_Textblock_Text_Item *ti);
3878 static Evas_Object_Textblock_Format_Item *_layout_do_format(const Evas_Object *obj EINA_UNUSED, Ctxt *c, Evas_Object_Textblock_Format **_fmt, Evas_Object_Textblock_Node_Format *n, int *style_pad_l, int *style_pad_r, int *style_pad_t, int *style_pad_b, Eina_Bool create_item);
3879 
3880 /**
3881  * @internal
3882  * Adjust the ascent/descent of the format and context.
3883  *
3884  * @param maxascent The ascent to update - Not NUL.
3885  * @param maxdescent The descent to update - Not NUL.
3886  * @param fmt The format to adjust - NOT NULL.
3887  */
3888 static void
_layout_format_ascent_descent_adjust(Evas_Object_Protected_Data * obj,Evas_Coord * maxascent,Evas_Coord * maxdescent,Evas_Object_Textblock_Format * fmt)3889 _layout_format_ascent_descent_adjust(Evas_Object_Protected_Data *obj,
3890       Evas_Coord *maxascent, Evas_Coord *maxdescent,
3891       Evas_Object_Textblock_Format *fmt)
3892 {
3893    int ascent, descent;
3894 
3895    if (fmt->font.font)
3896      {
3897         ascent = *maxascent;
3898         descent = *maxdescent;
3899         if (fmt->linesize > 0)
3900           {
3901              int scaled_linesize = fmt->linesize * obj->cur->scale;
3902              if ((ascent + descent) < scaled_linesize)
3903                {
3904                   ascent = ((scaled_linesize * ascent) / (ascent + descent));
3905                   descent = scaled_linesize - ascent;
3906                }
3907           }
3908         else if (fmt->linerelsize > 0.0)
3909           {
3910              descent = descent * fmt->linerelsize;
3911              ascent = ascent * fmt->linerelsize;
3912           }
3913         descent += fmt->linegap * obj->cur->scale;
3914         descent += ((ascent + descent) * fmt->linerelgap);
3915         *maxascent = ascent;
3916         *maxdescent = descent;
3917         if (fmt->linefill > 0.0)
3918           {
3919              int dh;
3920 
3921              dh = obj->cur->geometry.h - (*maxascent + *maxdescent);
3922              if (dh < 0) dh = 0;
3923              dh = fmt->linefill * dh;
3924              *maxdescent += dh / 2;
3925              *maxascent += dh - (dh / 2);
3926              // FIXME: set flag that says "if heigh changes - reformat"
3927           }
3928      }
3929 }
3930 
3931 static void
_layout_item_max_ascent_descent_calc(Evas_Object_Protected_Data * obj,Evas_Coord * maxascent,Evas_Coord * maxdescent,Evas_Object_Textblock_Item * it,Textblock_Position position)3932 _layout_item_max_ascent_descent_calc(Evas_Object_Protected_Data *obj,
3933       Evas_Coord *maxascent, Evas_Coord *maxdescent,
3934       Evas_Object_Textblock_Item *it, Textblock_Position position)
3935 {
3936    void *fi = NULL;
3937    *maxascent = *maxdescent = 0;
3938 
3939    if (!it || !it->format || !it->format->font.font)
3940       return;
3941 
3942    if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3943      {
3944         fi = _ITEM_TEXT(it)->text_props.font_instance;
3945      }
3946 
3947    if ((position == TEXTBLOCK_POSITION_START) ||
3948          (position == TEXTBLOCK_POSITION_SINGLE))
3949      {
3950         Evas_Coord asc = 0;
3951 
3952         *maxascent = ENFN->font_max_ascent_get(ENC, it->format->font.font);
3953 
3954         if (fi)
3955           asc = evas_common_font_instance_max_ascent_get(fi);
3956 
3957         if (asc > *maxascent)
3958           *maxascent = asc;
3959      }
3960 
3961    if ((position == TEXTBLOCK_POSITION_END) ||
3962          (position == TEXTBLOCK_POSITION_SINGLE))
3963      {
3964         /* Calculate max descent. */
3965         Evas_Coord desc = 0;
3966 
3967         *maxdescent = ENFN->font_max_descent_get(ENC, it->format->font.font);
3968 
3969         if (fi)
3970           desc = evas_common_font_instance_max_descent_get(fi);
3971 
3972         if (desc > *maxdescent)
3973           *maxdescent = desc;
3974      }
3975 }
3976 
3977 /**
3978  * @internal
3979  * Adjust the ascent/descent of the item and context.
3980  *
3981  * @param ascent The ascent to update - Not NUL.
3982  * @param descent The descent to update - Not NUL.
3983  * @param it The format to adjust - NOT NULL.
3984  * @param position The position inside the textblock
3985  */
3986 static void
_layout_item_ascent_descent_adjust(Evas_Object_Protected_Data * obj,Evas_Coord * ascent,Evas_Coord * descent,Evas_Object_Textblock_Item * it,Evas_Object_Textblock_Format * fmt)3987 _layout_item_ascent_descent_adjust(Evas_Object_Protected_Data *obj,
3988       Evas_Coord *ascent, Evas_Coord *descent,
3989       Evas_Object_Textblock_Item *it, Evas_Object_Textblock_Format *fmt)
3990 {
3991    void *fi = NULL;
3992    int asc = 0, desc = 0;
3993 
3994    if ((!it || !it->format || !it->format->font.font) &&
3995          (!fmt || !fmt->font.font))
3996      {
3997         return;
3998      }
3999 
4000    if (it)
4001      {
4002         fmt = it->format;
4003 
4004         if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
4005           {
4006              fi = _ITEM_TEXT(it)->text_props.font_instance;
4007           }
4008      }
4009 
4010    if (fmt)
4011      {
4012         asc = ENFN->font_ascent_get(ENC, fmt->font.font);
4013         desc = ENFN->font_descent_get(ENC, fmt->font.font);
4014      }
4015 
4016    if (fi)
4017      {
4018         int fi_asc = evas_common_font_instance_ascent_get(fi);
4019         int fi_desc = evas_common_font_instance_descent_get(fi);
4020 
4021         if (fi_asc > asc) asc = fi_asc;
4022         if (fi_desc > desc) desc = fi_desc;
4023      }
4024 
4025    if (fmt) _layout_format_ascent_descent_adjust(obj, &asc, &desc, fmt);
4026 
4027    if (asc > *ascent) *ascent = asc;
4028    if (desc > *descent) *descent = desc;
4029 }
4030 
4031 /**
4032  * @internal
4033  * Create a new line using the info from the format and update the format
4034  * and context.
4035  *
4036  * @param c The context to work on - Not NULL.
4037  * @param fmt The format to use info from - NOT NULL.
4038  */
4039 static void
_layout_line_new(Ctxt * c,Evas_Object_Textblock_Format * fmt)4040 _layout_line_new(Ctxt *c, Evas_Object_Textblock_Format *fmt)
4041 {
4042    c->ln = calloc(1, sizeof(Evas_Object_Textblock_Line));
4043    c->align = fmt->halign;
4044    c->align_auto = fmt->halign_auto;
4045    c->marginl = fmt->margin.l;
4046    c->marginr = fmt->margin.r;
4047    c->par->lines = (Evas_Object_Textblock_Line *)eina_inlist_append(EINA_INLIST_GET(c->par->lines), EINA_INLIST_GET(c->ln));
4048    c->x = 0;
4049    c->ascent = c->descent = 0;
4050    c->maxascent = c->maxdescent = 0;
4051    c->ln->line_no = -1;
4052    c->ln->par = c->par;
4053 }
4054 
4055 static inline Evas_Object_Textblock_Paragraph *
_layout_find_paragraph_by_y(Efl_Canvas_Textblock_Data * o,Evas_Coord y)4056 _layout_find_paragraph_by_y(Efl_Canvas_Textblock_Data *o, Evas_Coord y)
4057 {
4058    Evas_Object_Textblock_Paragraph *start, *par;
4059    int i;
4060 
4061    start = o->paragraphs;
4062 
4063    for (i = 0 ; i < TEXTBLOCK_PAR_INDEX_SIZE ; i++)
4064      {
4065         if (!o->par_index[i] || (o->par_index[i]->y > y))
4066           {
4067              break;
4068           }
4069         start = o->par_index[i];
4070      }
4071 
4072    EINA_INLIST_FOREACH(start, par)
4073      {
4074         if ((par->y <= y) && (y < par->y + par->h))
4075            return par;
4076      }
4077 
4078    return NULL;
4079 }
4080 
4081 static inline Evas_Object_Textblock_Paragraph *
_layout_find_paragraph_by_line_no(Efl_Canvas_Textblock_Data * o,int line_no)4082 _layout_find_paragraph_by_line_no(Efl_Canvas_Textblock_Data *o, int line_no)
4083 {
4084    Evas_Object_Textblock_Paragraph *start, *par;
4085    int i;
4086 
4087    start = o->paragraphs;
4088 
4089    for (i = 0 ; i < TEXTBLOCK_PAR_INDEX_SIZE ; i++)
4090      {
4091         if (!o->par_index[i] || (o->par_index[i]->line_no > line_no))
4092           {
4093              break;
4094           }
4095         start = o->par_index[i];
4096      }
4097 
4098    EINA_INLIST_FOREACH(start, par)
4099      {
4100         Evas_Object_Textblock_Paragraph *npar =
4101            (Evas_Object_Textblock_Paragraph *) EINA_INLIST_GET(par)->next;
4102         if ((par->line_no <= line_no) &&
4103               (!npar || (line_no < npar->line_no)))
4104            return par;
4105      }
4106 
4107    return NULL;
4108 }
4109 /* End of rbtree index functios */
4110 
4111 /**
4112  * @internal
4113  * Create a new layout paragraph.
4114  * If c->par is not NULL, the paragraph is appended/prepended according
4115  * to the append parameter. If it is NULL, the paragraph is appended at
4116  * the end of the list.
4117  *
4118  * @param c The context to work on - Not NULL.
4119  * @param n the associated text node
4120  * @param append true to append, false to prpend.
4121  * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
4122  */
4123 static Eina_Bool
_layout_paragraph_new(Ctxt * c,Evas_Object_Textblock_Node_Text * n,Eina_Bool append)4124 _layout_paragraph_new(Ctxt *c, Evas_Object_Textblock_Node_Text *n,
4125       Eina_Bool append)
4126 {
4127    Evas_Object_Textblock_Paragraph *rel_par = c->par;
4128    Evas_Object_Textblock_Paragraph *new_par = calloc(1, sizeof(Evas_Object_Textblock_Paragraph));
4129 
4130    if (!new_par) return EINA_FALSE;
4131    c->par = new_par;
4132 
4133    if (append || !rel_par)
4134       c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4135          eina_inlist_append_relative(EINA_INLIST_GET(c->paragraphs),
4136                EINA_INLIST_GET(c->par),
4137                EINA_INLIST_GET(rel_par));
4138    else
4139       c->paragraphs = (Evas_Object_Textblock_Paragraph *)
4140          eina_inlist_prepend_relative(EINA_INLIST_GET(c->paragraphs),
4141                EINA_INLIST_GET(c->par),
4142                EINA_INLIST_GET(rel_par));
4143 
4144    c->ln = NULL;
4145    c->par->text_node = n;
4146    if (n)
4147       n->par = c->par;
4148    c->par->line_no = -1;
4149    c->par->visible = 1;
4150    c->o->num_paragraphs++;
4151 
4152    return EINA_TRUE;
4153 }
4154 
4155 #ifdef BIDI_SUPPORT
4156 /**
4157  * @internal
4158  * Update bidi paragraph props.
4159  *
4160  * @param par The paragraph to update
4161  */
4162 static inline void
_layout_update_bidi_props(const Efl_Canvas_Textblock_Data * o,Evas_Object_Textblock_Paragraph * par)4163 _layout_update_bidi_props(const Efl_Canvas_Textblock_Data *o,
4164       Evas_Object_Textblock_Paragraph *par)
4165 {
4166    if (par->text_node)
4167      {
4168         const Eina_Unicode *text;
4169         int *segment_idxs = NULL;
4170         Evas_BiDi_Direction par_dir;
4171         EvasBiDiParType bidi_par_type;
4172 
4173         text = eina_ustrbuf_string_get(par->text_node->unicode);
4174 
4175         if (o->bidi_delimiters)
4176            segment_idxs = evas_bidi_segment_idxs_get(text, o->bidi_delimiters);
4177 
4178         par_dir = o->paragraph_direction;
4179 
4180         switch (par_dir)
4181           {
4182            case EVAS_BIDI_DIRECTION_LTR:
4183               bidi_par_type = EVAS_BIDI_PARAGRAPH_LTR;
4184               break;
4185            case EVAS_BIDI_DIRECTION_RTL:
4186               bidi_par_type = EVAS_BIDI_PARAGRAPH_RTL;
4187               break;
4188            case EVAS_BIDI_DIRECTION_NEUTRAL:
4189            default:
4190               bidi_par_type = EVAS_BIDI_PARAGRAPH_NEUTRAL;
4191               break;
4192           }
4193 
4194         evas_bidi_paragraph_props_unref(par->bidi_props);
4195         par->bidi_props = evas_bidi_paragraph_props_get(text,
4196               eina_ustrbuf_length_get(par->text_node->unicode),
4197               segment_idxs, bidi_par_type);
4198         par->direction = EVAS_BIDI_PARAGRAPH_DIRECTION_IS_RTL(par->bidi_props) ?
4199            EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
4200         par->is_bidi = !!par->bidi_props;
4201         if (segment_idxs) free(segment_idxs);
4202      }
4203 }
4204 #endif
4205 
4206 
4207 /**
4208  * @internal
4209  * Free the visual lines in the paragraph (logical items are kept)
4210  */
4211 static void
_paragraph_clear(Efl_Canvas_Textblock_Data * o,Evas_Object_Protected_Data * obj,Evas_Object_Textblock_Paragraph * par)4212 _paragraph_clear(Efl_Canvas_Textblock_Data *o,
4213       Evas_Object_Protected_Data *obj,
4214       Evas_Object_Textblock_Paragraph *par)
4215 {
4216    while (par->lines)
4217      {
4218         Evas_Object_Textblock_Line *ln;
4219 
4220         ln = (Evas_Object_Textblock_Line *) par->lines;
4221         par->lines = (Evas_Object_Textblock_Line *)eina_inlist_remove(EINA_INLIST_GET(par->lines), EINA_INLIST_GET(par->lines));
4222 
4223         /* Could be done better, but it's only when hyphenating and limited
4224          * to number of hyphens created */
4225         if (o->hyphenating)
4226           {
4227              Evas_Object_Textblock_Text_Item *ti;
4228              Eina_List *i, *i_next;
4229 
4230              EINA_LIST_FOREACH_SAFE(o->hyphen_items, i, i_next, ti)
4231                {
4232                   if (ti->parent.ln == ln)
4233                     {
4234                        o->hyphen_items = eina_list_remove_list(o->hyphen_items, i);
4235                        _item_free(o, obj, NULL, _ITEM(ti));
4236                     }
4237                }
4238           }
4239 
4240         _line_free(ln);
4241      }
4242 }
4243 
4244 /**
4245  * @internal
4246  * Free the layout paragraph and all of it's lines and logical items.
4247  */
4248 static void
_paragraph_free(Efl_Canvas_Textblock_Data * o,Evas_Object_Protected_Data * obj,Evas_Object_Textblock_Paragraph * par)4249 _paragraph_free(Efl_Canvas_Textblock_Data *o,
4250       Evas_Object_Protected_Data *obj,
4251       Evas_Object_Textblock_Paragraph *par)
4252 {
4253    _paragraph_clear(o, obj, par);
4254 
4255      {
4256         Evas_Object_Textblock_Item *it;
4257         EINA_LIST_FREE(par->logical_items, it)
4258           {
4259              _item_free(o, obj, NULL, it);
4260           }
4261      }
4262 #ifdef BIDI_SUPPORT
4263    if (par->bidi_props)
4264       evas_bidi_paragraph_props_unref(par->bidi_props);
4265 #endif
4266    /* If we are the active par of the text node, set to NULL */
4267    if (par->text_node && (par->text_node->par == par))
4268       par->text_node->par = NULL;
4269 
4270    o->num_paragraphs--;
4271 
4272    free(par);
4273 }
4274 
4275 /**
4276  * @internal
4277  * Clear all the paragraphs from the inlist pars.
4278  *
4279  * @param obj the evas object - Not NULL.
4280  * @param pars the paragraphs to clean - Not NULL.
4281  */
4282 static void
_paragraphs_clear(Ctxt * c)4283 _paragraphs_clear(Ctxt *c)
4284 {
4285    Evas_Object_Textblock_Paragraph *par;
4286 
4287    EINA_INLIST_FOREACH(EINA_INLIST_GET(c->paragraphs), par)
4288      {
4289         _paragraph_clear(c->o, c->evas_o, par);
4290      }
4291 }
4292 
4293 /**
4294  * @internal
4295  * Free the paragraphs from the inlist pars, the difference between this and
4296  * _paragraphs_clear is that the latter keeps the logical items and the par
4297  * items, while the former frees them as well.
4298  *
4299  * @param c the context - Not NULL.
4300  */
4301 static void
_paragraphs_free(Efl_Canvas_Textblock_Data * o,Evas_Object_Protected_Data * obj,Evas_Object_Textblock_Paragraph * pars)4302 _paragraphs_free(Efl_Canvas_Textblock_Data *o,
4303       Evas_Object_Protected_Data *obj,
4304       Evas_Object_Textblock_Paragraph *pars)
4305 {
4306    o->num_paragraphs = 0;
4307 
4308    while (pars)
4309      {
4310         Evas_Object_Textblock_Paragraph *par;
4311 
4312         par = (Evas_Object_Textblock_Paragraph *) pars;
4313         pars = (Evas_Object_Textblock_Paragraph *)eina_inlist_remove(EINA_INLIST_GET(pars), EINA_INLIST_GET(par));
4314         _paragraph_free(o, obj, par);
4315      }
4316 }
4317 
4318 /**
4319  * @internal
4320  * Push fmt to the format stack, if fmt is NULL, will push a default item.
4321  *
4322  * @param c the context to work on - Not NULL.
4323  * @param fmt the format to push.
4324  * @see _layout_format_pop()
4325  */
4326 static Evas_Object_Textblock_Format *
_layout_format_push(Ctxt * c,Evas_Object_Textblock_Format * fmt,Evas_Object_Textblock_Node_Format * fnode)4327 _layout_format_push(Ctxt *c, Evas_Object_Textblock_Format *fmt,
4328       Evas_Object_Textblock_Node_Format *fnode)
4329 {
4330    Efl_Canvas_Textblock_Data *o = c->o;
4331 
4332    if (fmt)
4333      {
4334         fmt = _format_dup(c->obj, fmt);
4335         c->format_stack  = eina_list_prepend(c->format_stack, fmt);
4336         fmt->fnode = fnode;
4337      }
4338    else
4339      {
4340         fmt = calloc(1, sizeof(Evas_Object_Textblock_Format));
4341         c->format_stack  = eina_list_prepend(c->format_stack, fmt);
4342         *fmt = c->o->default_format.format;
4343         fmt->ref = 1;
4344         fmt->font.bitmap_scalable = _FMT_INFO(bitmap_scalable);
4345 
4346         // Apply font if specified
4347         if (_FMT_INFO(font))
4348           {
4349              Evas_Object_Protected_Data *evas_obj = efl_data_scope_get(c->obj, EFL_CANVAS_OBJECT_CLASS);
4350 
4351              if (fmt->font.fdesc)
4352                {
4353                   evas_font_desc_unref(fmt->font.fdesc);
4354                }
4355              fmt->font.fdesc = evas_font_desc_new();
4356 
4357              eina_stringshare_replace(&(fmt->font.fdesc->lang),
4358                    evas_font_lang_normalize(_FMT_INFO(font_lang)));
4359              eina_stringshare_replace(&(fmt->font.fdesc->fallbacks),
4360                    _FMT_INFO(font_fallbacks));
4361 
4362              fmt->font.size = _FMT_INFO(size);
4363              fmt->font.fdesc->weight = _FMT_INFO(font_weight);
4364              fmt->font.fdesc->slant = _FMT_INFO(font_slant);
4365              fmt->font.fdesc->width = _FMT_INFO(font_width);
4366              eina_stringshare_replace(&(fmt->font.source), _FMT_INFO(font_source));
4367              evas_font_name_parse(fmt->font.fdesc, _FMT_INFO(font));
4368              fmt->font.font = evas_font_load(evas_obj->layer->evas->font_path,
4369                                              evas_obj->layer->evas->hinting,
4370                                              fmt->font.fdesc,
4371                                              fmt->font.source,
4372                                              (int)(((double) _FMT_INFO(size)) * evas_obj->cur->scale),
4373                                              fmt->font.bitmap_scalable);
4374           }
4375         if (_FMT_INFO(gfx_filter_name))
4376           {
4377              if (!fmt->gfx_filter)
4378                {
4379                   fmt->gfx_filter = calloc(1, sizeof(Efl_Canvas_Textblock_Filter));
4380                   eina_stringshare_replace(&fmt->gfx_filter->name,
4381                         _FMT_INFO(gfx_filter_name));
4382                }
4383           }
4384      }
4385 
4386    return fmt;
4387 }
4388 
4389 /**
4390  * @internal
4391  * Pop fmt to the format stack, if there's something in the stack free fmt
4392  * and set it to point to the next item instead, else return fmt.
4393  *
4394  * @param c the context to work on - Not NULL.
4395  * @param format - the text of the format to free (assured to start with '-').
4396  * @return the next format in the stack, or format if there's none.
4397  * @see _layout_format_push()
4398  */
4399 static Evas_Object_Textblock_Format *
_layout_format_pop(Ctxt * c,const char * format)4400 _layout_format_pop(Ctxt *c, const char *format)
4401 {
4402    Evas_Object_Textblock_Format *fmt = eina_list_data_get(c->format_stack);
4403 
4404    if ((c->format_stack) && (c->format_stack->next))
4405      {
4406         Eina_List *redo_nodes = NULL;
4407 
4408         /* Generic pop, should just pop. */
4409         if (((format[0] == '/') && !format[1]) ||
4410               !format[0])
4411           {
4412              _format_unref_free(c->evas_o, fmt);
4413              c->format_stack =
4414                 eina_list_remove_list(c->format_stack, c->format_stack);
4415           }
4416         else
4417           {
4418              size_t len = strlen(format);
4419              Eina_List *i, *i_next;
4420              /* Remove only the matching format. */
4421              EINA_LIST_FOREACH_SAFE(c->format_stack, i, i_next, fmt)
4422                {
4423                   /* Stop when we reach the base item */
4424                   if (!i_next)
4425                      break;
4426 
4427                   c->format_stack =
4428                      eina_list_remove_list(c->format_stack, c->format_stack);
4429 
4430                   /* Make sure the ending tag matches the starting tag.
4431                    * I.e whole of the ending tag matches the start of the
4432                    * starting tag, and the starting tag's next char is either
4433                    * NULL or white. Skip the starting '+'. */
4434                   if (_FORMAT_IS_CLOSER_OF(
4435                            fmt->fnode->orig_format, format + 1, len - 1))
4436                     {
4437                        _format_unref_free(c->evas_o, fmt);
4438                        break;
4439                     }
4440                   else
4441                     {
4442                        redo_nodes = eina_list_prepend(redo_nodes, fmt->fnode);
4443                        _format_unref_free(c->evas_o, fmt);
4444                     }
4445                }
4446           }
4447 
4448         /* Redo all the nodes needed to be redone */
4449           {
4450              Evas_Object_Textblock_Node_Format *fnode;
4451              Eina_List *i, *i_next;
4452 
4453              EINA_LIST_FOREACH_SAFE(redo_nodes, i, i_next, fnode)
4454                {
4455                   /* FIXME: Actually do something with the new acquired padding,
4456                    * they can be different and affect our padding! */
4457                   Evas_Coord style_pad_l, style_pad_r, style_pad_t, style_pad_b;
4458                   style_pad_l = style_pad_r = style_pad_t = style_pad_b = 0;
4459                   redo_nodes = eina_list_remove_list(redo_nodes, i);
4460                   fmt = eina_list_data_get(c->format_stack);
4461                   _layout_do_format(c->obj, c, &fmt, fnode,
4462                         &style_pad_l, &style_pad_r,
4463                         &style_pad_t, &style_pad_b, EINA_FALSE);
4464                }
4465           }
4466 
4467         fmt = eina_list_data_get(c->format_stack);
4468      }
4469    return fmt;
4470 }
4471 
4472 /**
4473  * @internal
4474  * Parse item and fill fmt with the item.
4475  *
4476  * @param c the context to work on - Not NULL.
4477  * @param fmt the format to fill - not null.
4478  */
4479 static void
_layout_format_value_handle(Ctxt * c,Evas_Object_Textblock_Format * fmt,const char * item)4480 _layout_format_value_handle(Ctxt *c, Evas_Object_Textblock_Format *fmt, const char *item)
4481 {
4482    const char *key = NULL;
4483    char *val = NULL;
4484 
4485    Allocator allocator;
4486    _allocator_init(&allocator);
4487 
4488    _format_param_parse(item, &key, &val, &allocator);
4489    if ((key) && (val)) _format_command(c->obj, fmt, key, val);
4490    if (key) eina_stringshare_del(key);
4491 
4492    _allocator_reset(&allocator);
4493 
4494    c->align = fmt->halign;
4495    c->align_auto = fmt->halign_auto;
4496    c->marginl = fmt->margin.l;
4497    c->marginr = fmt->margin.r;
4498 }
4499 
4500 #define VSIZE_FULL 0
4501 #define VSIZE_ASCENT 1
4502 
4503 #define SIZE 0
4504 #define SIZE_ABS 1
4505 #define SIZE_REL 2
4506 
4507 /**
4508  * @internal
4509  * Get the current line's alignment from the context.
4510  *
4511  * @param c the context to work on - Not NULL.
4512  */
4513 static inline double
_layout_line_align_get(Ctxt * c)4514 _layout_line_align_get(Ctxt *c)
4515 {
4516 #ifdef BIDI_SUPPORT
4517    if ((c->align_auto == EVAS_TEXTBLOCK_ALIGN_AUTO_NORMAL) && c->ln)
4518      {
4519         if (c->ln->items && c->ln->items->text_node &&
4520               (c->ln->par->direction == EVAS_BIDI_DIRECTION_RTL))
4521           {
4522              /* Align right*/
4523              return 1.0;
4524           }
4525         else
4526           {
4527              /* Align left */
4528              return 0.0;
4529           }
4530      }
4531    else if ((c->align_auto == EVAS_TEXTBLOCK_ALIGN_AUTO_END) && c->ln)
4532      {
4533         if (c->ln->items && c->ln->items->text_node &&
4534               (c->ln->par->direction == EVAS_BIDI_DIRECTION_RTL))
4535           {
4536              /* Align left*/
4537              return 0.0;
4538           }
4539         else
4540           {
4541              /* Align right */
4542              return 1.0;
4543           }
4544      }
4545    else if (c->align_auto == EVAS_TEXTBLOCK_ALIGN_AUTO_LOCALE)
4546      {
4547         if (evas_common_language_direction_get() == EVAS_BIDI_DIRECTION_RTL)
4548           {
4549              /* Align right*/
4550              return 1.0;
4551           }
4552         else
4553           {
4554              /* Align left */
4555              return 0.0;
4556           }
4557      }
4558 
4559 #endif
4560    return c->align;
4561 }
4562 
4563 #ifdef BIDI_SUPPORT
4564 /**
4565  * @internal
4566  * Reorder the items in visual order
4567  *
4568  * @param line the line to reorder
4569  */
4570 static void
_layout_line_reorder(Evas_Object_Textblock_Line * line)4571 _layout_line_reorder(Evas_Object_Textblock_Line *line)
4572 {
4573    /*FIXME: do it a bit more efficient - not very efficient ATM. */
4574    Evas_Object_Textblock_Item *it;
4575    EvasBiDiStrIndex *v_to_l = NULL;
4576    Evas_Coord x;
4577    size_t start, end;
4578    size_t len;
4579 
4580    if (line->items && line->items->text_node &&
4581          line->par->bidi_props)
4582      {
4583         Evas_BiDi_Paragraph_Props *props;
4584         props = line->par->bidi_props;
4585         start = end = line->items->text_pos;
4586 
4587         /* Find the first and last positions in the line */
4588 
4589         EINA_INLIST_FOREACH(line->items, it)
4590           {
4591              if (it->text_pos < start)
4592                {
4593                   start = it->text_pos;
4594                }
4595              else
4596                {
4597                   int tlen;
4598                   tlen = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
4599                      _ITEM_TEXT(it)->text_props.text_len : 1;
4600                   if (it->text_pos + tlen > end)
4601                     {
4602                        end = it->text_pos + tlen;
4603                     }
4604                }
4605           }
4606 
4607         len = end - start;
4608         evas_bidi_props_reorder_line(NULL, start, len, props, &v_to_l);
4609 
4610         /* Update visual pos */
4611           {
4612              Evas_Object_Textblock_Item *i;
4613              i = line->items;
4614              while (i)
4615                {
4616                   i->visual_pos = evas_bidi_position_logical_to_visual(
4617                         v_to_l, len, i->text_pos - start);
4618                   i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(i)->next;
4619                }
4620           }
4621 
4622         /*FIXME: not very efficient, sort the items arrays. Anyhow, should only
4623          * reorder if it's a bidi paragraph */
4624           {
4625              Evas_Object_Textblock_Item *i, *j, *min;
4626              i = line->items;
4627              while (i)
4628                {
4629                   min = i;
4630                   EINA_INLIST_FOREACH(i, j)
4631                     {
4632                        if (j->visual_pos < min->visual_pos)
4633                          {
4634                             min = j;
4635                          }
4636                     }
4637                   if (min != i)
4638                     {
4639                        line->items = (Evas_Object_Textblock_Item *) eina_inlist_remove(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min));
4640                        line->items = (Evas_Object_Textblock_Item *) eina_inlist_prepend_relative(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min), EINA_INLIST_GET(i));
4641                     }
4642 
4643                   i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(min)->next;
4644                }
4645           }
4646      }
4647 
4648    if (v_to_l) free(v_to_l);
4649    x = 0;
4650    EINA_INLIST_FOREACH(line->items, it)
4651      {
4652         it->x = x;
4653         x += it->adv;
4654      }
4655 }
4656 #endif
4657 
4658 /* FIXME: doc */
4659 static void
_layout_calculate_format_item_size(Evas_Object_Protected_Data * obj,const Evas_Object_Textblock_Format_Item * fi,Evas_Coord * maxascent,Evas_Coord * maxdescent,Evas_Coord * _y,Evas_Coord * _w,Evas_Coord * _h)4660 _layout_calculate_format_item_size(Evas_Object_Protected_Data *obj,
4661       const Evas_Object_Textblock_Format_Item *fi,
4662       Evas_Coord *maxascent, Evas_Coord *maxdescent,
4663       Evas_Coord *_y, Evas_Coord *_w, Evas_Coord *_h)
4664 {
4665    /* Adjust sizes according to current line height/scale */
4666    Evas_Coord w, h;
4667    const char *p, *s;
4668 
4669    s = fi->item;
4670    w = fi->parent.w;
4671    h = fi->parent.h;
4672    if (!s)
4673      {
4674         *_w = w;
4675         *_h = h;
4676         return;
4677      }
4678    switch (fi->size)
4679      {
4680       case SIZE:
4681          p = strstr(s, " size=");
4682          if (p)
4683            {
4684               p += 6;
4685               if (sscanf(p, "%ix%i", &w, &h) == 2)
4686                 {
4687                    w = w * obj->cur->scale;
4688                    h = h * obj->cur->scale;
4689                 }
4690            }
4691          break;
4692       case SIZE_REL:
4693          p = strstr(s, " relsize=");
4694          p += 9;
4695          if (sscanf(p, "%ix%i", &w, &h) == 2)
4696            {
4697               int sz = 1;
4698               if (fi->vsize == VSIZE_FULL)
4699                 {
4700                    sz = *maxdescent + *maxascent;
4701                 }
4702               else if (fi->vsize == VSIZE_ASCENT)
4703                 {
4704                    sz = *maxascent;
4705                 }
4706               w = (w * sz) / h;
4707               h = sz;
4708            }
4709          break;
4710       case SIZE_ABS:
4711          /* Nothing to do */
4712       default:
4713          break;
4714      }
4715 
4716    switch (fi->size)
4717      {
4718       case SIZE:
4719       case SIZE_ABS:
4720          switch (fi->vsize)
4721            {
4722             case VSIZE_FULL:
4723                if (h > (*maxdescent + *maxascent))
4724                  {
4725                     *maxascent += h - (*maxdescent + *maxascent);
4726                     *_y = -*maxascent;
4727                  }
4728                else
4729                   *_y = -(h - *maxdescent);
4730                break;
4731             case VSIZE_ASCENT:
4732                if (h > *maxascent)
4733                  {
4734                     *maxascent = h;
4735                     *_y = -h;
4736                  }
4737                else
4738                   *_y = -h;
4739                break;
4740             default:
4741                break;
4742            }
4743          break;
4744       case SIZE_REL:
4745          switch (fi->vsize)
4746            {
4747             case VSIZE_FULL:
4748             case VSIZE_ASCENT:
4749                *_y = -*maxascent;
4750                break;
4751             default:
4752                break;
4753            }
4754          break;
4755       default:
4756          break;
4757      }
4758 
4759    *_w = w;
4760    *_h = h;
4761 }
4762 
4763 static Evas_Coord
_layout_last_line_max_descent_adjust_calc(Ctxt * c,const Evas_Object_Textblock_Paragraph * last_vis_par)4764 _layout_last_line_max_descent_adjust_calc(Ctxt *c, const Evas_Object_Textblock_Paragraph *last_vis_par)
4765 {
4766    if (last_vis_par->lines)
4767      {
4768         Evas_Object_Textblock_Line *ln = (Evas_Object_Textblock_Line *)
4769            EINA_INLIST_GET(last_vis_par->lines)->last;
4770         Evas_Object_Textblock_Item *it;
4771 
4772         EINA_INLIST_FOREACH(ln->items, it)
4773           {
4774              if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
4775                {
4776                   Evas_Coord asc = 0, desc = 0;
4777                   Evas_Coord maxasc = 0, maxdesc = 0;
4778                   _layout_item_ascent_descent_adjust(c->evas_o, &asc, &desc,
4779                         it, it->format);
4780                   _layout_item_max_ascent_descent_calc(c->evas_o, &maxasc, &maxdesc,
4781                         it, c->position);
4782 
4783                   if (desc > c->descent)
4784                      c->descent = desc;
4785                   if (maxdesc > c->maxdescent)
4786                      c->maxdescent = maxdesc;
4787                }
4788           }
4789 
4790         if (c->maxdescent > c->descent)
4791           {
4792              return c->maxdescent - c->descent;
4793           }
4794      }
4795 
4796    return 0;
4797 }
4798 typedef struct _Evas_Textblock_Obstacle_Info
4799 {
4800    Evas_Object_Textblock_Item *it; /**< the corresponding item node. */
4801    Evas_Coord obs_adv;
4802    Evas_Coord obs_preadv;
4803 } Evas_Textblock_Obstacle_Info;
4804 
4805 /**
4806  * @internal
4807  * Order the items in the line, update it's properties and update it's
4808  * corresponding paragraph.
4809  *
4810  * @param c the context to work on - Not NULL.
4811  * @param fmt the format to use.
4812  * @param add_line true if we should create a line, false otherwise.
4813  */
4814 static void
_layout_line_finalize(Ctxt * c,Evas_Object_Textblock_Format * fmt)4815 _layout_line_finalize(Ctxt *c, Evas_Object_Textblock_Format *fmt)
4816 {
4817    Evas_Object_Textblock_Item *it;
4818    Evas_Coord obs_preadv = 0, obs_adv = 0;
4819    Eina_List *i;
4820    Evas_Textblock_Obstacle_Info *obs_info = NULL;
4821    Evas_Coord x = 0;
4822 
4823    /* If there are no text items yet, calc ascent/descent
4824     * according to the current format. */
4825    if (c->ascent + c->descent == 0)
4826       _layout_item_ascent_descent_adjust(c->evas_o, &c->ascent, &c->descent,
4827             NULL, fmt);
4828 
4829 #ifdef BIDI_SUPPORT
4830    _layout_line_reorder(c->ln);
4831 #endif
4832 
4833    /* Adjust all the item sizes according to the final line size,
4834     * and update the x positions of all the items of the line. */
4835    EINA_INLIST_FOREACH(c->ln->items, it)
4836      {
4837         if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
4838           {
4839              Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
4840              if (!fi->formatme) goto loop_advance;
4841              _layout_calculate_format_item_size(c->evas_o, fi, &c->ascent,
4842                    &c->descent, &fi->y, &fi->parent.w, &fi->parent.h);
4843              fi->parent.adv = fi->parent.w;
4844           }
4845         else
4846           {
4847              Evas_Coord asc = 0, desc = 0;
4848              Evas_Coord maxasc = 0, maxdesc = 0;
4849 
4850              _layout_item_ascent_descent_adjust(c->evas_o, &asc, &desc,
4851                    it, it->format);
4852              _layout_item_max_ascent_descent_calc(c->evas_o, &maxasc, &maxdesc,
4853                    it, c->position);
4854 
4855              if (asc > c->ascent)
4856                 c->ascent = asc;
4857              if (desc > c->descent)
4858                 c->descent = desc;
4859              if (maxasc > c->maxascent)
4860                 c->maxascent = maxasc;
4861              if (maxdesc > c->maxdescent)
4862                 c->maxdescent = maxdesc;
4863           }
4864 
4865 loop_advance:
4866         obs_preadv = 0;
4867         obs_adv = 0;
4868         EINA_LIST_FOREACH(c->obs_infos, i, obs_info)
4869           {
4870              if (obs_info->it == it)
4871                {
4872                   obs_preadv += obs_info->obs_preadv;
4873                   obs_adv += obs_info->obs_adv;
4874                }
4875           }
4876         x += obs_preadv;
4877         it->x = x;
4878         x += it->adv + obs_adv;
4879 
4880         if ((it->w > 0) && ((it->x + it->w) > c->ln->w)) c->ln->w = it->x + it->w;
4881      }
4882 
4883    /* clear obstacle info for this line */
4884    EINA_LIST_FREE(c->obs_infos, obs_info)
4885      {
4886         free(obs_info);
4887      }
4888    c->ln->y = c->y - c->par->y;
4889    c->ln->h = c->ascent + c->descent;
4890 
4891    /* Handle max ascent and descent if at the edges */
4892      {
4893         /* If it's the start, offset the line according to the max ascent. */
4894         if (((c->position == TEXTBLOCK_POSITION_START) ||
4895                  (c->position == TEXTBLOCK_POSITION_SINGLE))
4896               && (c->maxascent > c->ascent))
4897           {
4898              Evas_Coord ascdiff;
4899 
4900              ascdiff = c->maxascent - c->ascent;
4901              c->ln->y += ascdiff;
4902              c->y += ascdiff;
4903              c->ln->y += c->o->style_pad.t;
4904              c->y += c->o->style_pad.t;
4905           }
4906 
4907         if ((c->position == TEXTBLOCK_POSITION_END) ||
4908               (c->position == TEXTBLOCK_POSITION_SINGLE))
4909           {
4910              c->ln->h += c->o->style_pad.b;
4911           }
4912      }
4913 
4914    /* Check current line's height is acceptable or not */
4915    if (EINA_DBL_EQ(fmt->ellipsis, 1.0) &&
4916        (c->h > 0) && (c->y + c->ln->h > c->h))
4917      {
4918         /* No text is shown when the object height is smaller than actual font size's height.
4919          * Vertical ellipsis is not handled if the object has only one line. */
4920         if ((EINA_INLIST_GET(c->paragraphs) != EINA_INLIST_GET(c->par)) ||
4921             EINA_INLIST_GET(c->par->lines) != EINA_INLIST_GET(c->ln))
4922           {
4923              if (((c->position == TEXTBLOCK_POSITION_START) ||
4924                   (c->position == TEXTBLOCK_POSITION_SINGLE))
4925                  && (c->maxascent > c->ascent))
4926                c->y -= c->o->style_pad.t;
4927 
4928              /* Remove current line */
4929              c->par->lines = (Evas_Object_Textblock_Line *)eina_inlist_remove(
4930                 EINA_INLIST_GET(c->par->lines), EINA_INLIST_GET(c->ln));
4931 
4932              if (c->o->ellip_ti && (_ITEM(c->o->ellip_ti)->ln == c->ln))
4933                _ITEM(c->o->ellip_ti)->ln = NULL;
4934 
4935              _line_free(c->ln);
4936              c->ln = NULL;
4937              c->vertical_ellipsis = EINA_TRUE;
4938 
4939              return;
4940           }
4941      }
4942 
4943    c->ln->baseline = c->ascent;
4944    /* FIXME: Actually needs to be adjusted using the actual font value.
4945     * Also, underline_extend is actually not being used. */
4946    if (c->have_underline2)
4947      {
4948         if (c->descent < 4) c->underline_extend = 4 - c->descent;
4949      }
4950    else if (c->have_underline)
4951      {
4952         if (c->descent < 2) c->underline_extend = 2 - c->descent;
4953      }
4954    c->ln->line_no = c->line_no - c->ln->par->line_no;
4955    if ( c->line_no == 0 || c->o->multiline)
4956      {
4957         c->line_no++;
4958         c->y += c->ascent + c->descent;
4959      }
4960    if (c->w >= 0)
4961      {
4962         /* c->o->style_pad.r is already included in the line width, so it's
4963          * not used in this calculation. . */
4964         c->ln->x = c->marginl + c->o->style_pad.l +
4965            ((c->w - c->ln->w - c->o->style_pad.l - c->o->style_pad.r -
4966              c->marginl - c->marginr) * _layout_line_align_get(c));
4967      }
4968    else
4969      {
4970         c->ln->x = c->marginl + c->o->style_pad.l;
4971      }
4972 
4973    c->par->h = c->ln->y + c->ln->h;
4974    if (c->ln->w > c->par->w)
4975      {
4976         c->par->w = c->ln->w;
4977      }
4978 
4979    /* Calculate new max width */
4980      {
4981         Evas_Coord new_wmax = c->ln->w + c->marginl + c->marginr;
4982         if (new_wmax > c->par->last_fw)
4983            c->par->last_fw = new_wmax;
4984         if (new_wmax > c->wmax)
4985            c->wmax = new_wmax;
4986      }
4987 
4988    if (c->position == TEXTBLOCK_POSITION_START)
4989       c->position = TEXTBLOCK_POSITION_ELSE;
4990 }
4991 
4992 /**
4993  * @internal
4994  * Create a new line and append it to the lines in the context.
4995  *
4996  * @param c the context to work on - Not NULL.
4997  * @param fmt the format to use.
4998  * @param add_line true if we should create a line, false otherwise.
4999  */
5000 static void
_layout_line_advance(Ctxt * c,Evas_Object_Textblock_Format * fmt)5001 _layout_line_advance(Ctxt *c, Evas_Object_Textblock_Format *fmt)
5002 {
5003    Evas_Object_Textblock_Format *last_fmt = fmt;
5004 
5005    if (c->hyphen_ti)
5006      {
5007         c->ln->items = (Evas_Object_Textblock_Item *)
5008            eina_inlist_append(EINA_INLIST_GET(c->ln->items),
5009                  EINA_INLIST_GET(_ITEM(c->hyphen_ti)));
5010         c->hyphen_ti->parent.ln = c->ln;
5011         c->o->hyphen_items =
5012            eina_list_append(c->o->hyphen_items, c->hyphen_ti);
5013         c->hyphen_ti = NULL;
5014      }
5015    if (c->ln->items)
5016      {
5017         last_fmt = _ITEM(EINA_INLIST_GET(c->ln->items)->last)->format;
5018      }
5019    _layout_line_finalize(c, last_fmt);
5020 
5021    if (!c->vertical_ellipsis)
5022      _layout_line_new(c, fmt);
5023 }
5024 
5025 /**
5026  * @internal
5027  * Create a new text layout item from the string and the format.
5028  *
5029  * @param c the context to work on - Not NULL.
5030  * @param fmt the format to use.
5031  * @param str the string to use.
5032  * @param len the length of the string.
5033  */
5034 static Evas_Object_Textblock_Text_Item *
_layout_text_item_new(Ctxt * c EINA_UNUSED,Evas_Object_Textblock_Format * fmt)5035 _layout_text_item_new(Ctxt *c EINA_UNUSED, Evas_Object_Textblock_Format *fmt)
5036 {
5037    Evas_Object_Textblock_Text_Item *ti;
5038 
5039    ti = calloc(1, sizeof(Evas_Object_Textblock_Text_Item));
5040    ti->parent.format = fmt;
5041    ti->parent.format->ref++;
5042    ti->parent.type = EVAS_TEXTBLOCK_ITEM_TEXT;
5043    return ti;
5044 }
5045 
5046 /**
5047  * @internal
5048  * Return the cutoff of the text in the text item.
5049  *
5050  * @param c the context to work on - Not NULL.
5051  * @param fmt the format to use. - Not NULL.
5052  * @param it the item to check - Not null.
5053  * @return -1 if there is no cutoff (either because there is really none,
5054  * or because of an error), cutoff index on success.
5055  */
5056 static int
_layout_text_cutoff_get(Ctxt * c,Evas_Object_Textblock_Format * fmt,const Evas_Object_Textblock_Text_Item * ti,Evas_Coord w,Evas_Coord from_x,int width_offset)5057 _layout_text_cutoff_get(Ctxt *c, Evas_Object_Textblock_Format *fmt,
5058       const Evas_Object_Textblock_Text_Item *ti,
5059       Evas_Coord w, Evas_Coord from_x, int width_offset)
5060 {
5061    if (fmt->font.font)
5062      {
5063         Evas_Coord x;
5064         Evas_Object_Protected_Data *obj = c->evas_o;
5065 
5066         x = w - c->o->style_pad.l - c->o->style_pad.r - c->marginl -
5067            c->marginr - from_x;
5068 
5069         if (x < 0)
5070           x = 0;
5071         return ENFN->font_last_up_to_pos(ENC, fmt->font.font,
5072               &ti->text_props, x, 0, width_offset);
5073      }
5074    return -1;
5075 }
5076 
5077 static Evas_Object_Textblock_Text_Item * _layout_hyphen_item_new(Ctxt *c, const Evas_Object_Textblock_Text_Item *cur_ti);
5078 
5079 /**
5080  * @internal
5081  * Split before cut, and strip if str[cut - 1] is a whitespace.
5082  *
5083  * @param c the context to work on - Not NULL.
5084  * @param ti the item to cut - not null.
5085  * @param lti the logical list item of the item.
5086  * @param cut the cut index.
5087  * @return the second (newly created) item.
5088  */
5089 static Evas_Object_Textblock_Text_Item *
_layout_item_text_split_strip_white(Ctxt * c,Evas_Object_Textblock_Text_Item * ti,Eina_List * lti,size_t cut)5090 _layout_item_text_split_strip_white(Ctxt *c,
5091       Evas_Object_Textblock_Text_Item *ti, Eina_List *lti, size_t cut)
5092 {
5093    const Eina_Unicode *ts;
5094    Evas_Object_Textblock_Text_Item *new_ti = NULL, *white_ti = NULL;
5095 
5096    ts = GET_ITEM_TEXT(ti);
5097 
5098    if (!IS_AT_END(ti, cut) && (ti->text_props.text_len > 0))
5099      {
5100         new_ti = _layout_text_item_new(c, ti->parent.format);
5101         new_ti->parent.text_node = ti->parent.text_node;
5102         new_ti->parent.text_pos = ti->parent.text_pos + cut;
5103         new_ti->parent.merge = EINA_TRUE;
5104 
5105         evas_common_text_props_split(&ti->text_props,
5106                                      &new_ti->text_props, cut);
5107         _layout_text_add_logical_item(c, new_ti, lti);
5108      }
5109 
5110    /* Strip the previous white if needed */
5111    if ((cut >= 1) && _is_white(ts[cut - 1]) && (ti->text_props.text_len > 0))
5112      {
5113         if (cut - 1 > 0)
5114           {
5115              size_t white_cut = cut - 1;
5116              white_ti = _layout_text_item_new(c, ti->parent.format);
5117              white_ti->parent.text_node = ti->parent.text_node;
5118              white_ti->parent.text_pos = ti->parent.text_pos + white_cut;
5119              white_ti->parent.merge = EINA_TRUE;
5120              white_ti->parent.visually_deleted = EINA_TRUE;
5121 
5122              evas_common_text_props_split(&ti->text_props,
5123                    &white_ti->text_props, white_cut);
5124              _layout_text_add_logical_item(c, white_ti, lti);
5125           }
5126         else
5127           {
5128              /* Mark this one as the visually deleted. */
5129              ti->parent.visually_deleted = EINA_TRUE;
5130           }
5131      }
5132 
5133    if (new_ti || white_ti)
5134      {
5135         _text_item_update_sizes(c, ti);
5136      }
5137    return new_ti;
5138 }
5139 
5140 /**
5141  * @internal
5142  * Merge item2 into item1 and free item2.
5143  *
5144  * @param c the context to work on - Not NULL.
5145  * @param item1 the item to copy to
5146  * @param item2 the item to copy from
5147  */
5148 static void
_layout_item_merge_and_free(Ctxt * c,Evas_Object_Textblock_Text_Item * item1,Evas_Object_Textblock_Text_Item * item2)5149 _layout_item_merge_and_free(Ctxt *c,
5150       Evas_Object_Textblock_Text_Item *item1,
5151       Evas_Object_Textblock_Text_Item *item2)
5152 {
5153    evas_common_text_props_merge(&item1->text_props,
5154          &item2->text_props);
5155 
5156    _text_item_update_sizes(c, item1);
5157 
5158    item1->parent.merge = EINA_FALSE;
5159    item1->parent.visually_deleted = EINA_FALSE;
5160 
5161    _item_free(c->o, c->evas_o, NULL, _ITEM(item2));
5162 }
5163 
5164 /**
5165  * @internal
5166  * Calculates an item's size.
5167  *
5168  * @param c the context
5169  * @param it the item itself.
5170  */
5171 static void
_text_item_update_sizes(Ctxt * c,Evas_Object_Textblock_Text_Item * ti)5172 _text_item_update_sizes(Ctxt *c, Evas_Object_Textblock_Text_Item *ti)
5173 {
5174    int tw = 0, th = 0, advw = 0;
5175    const Evas_Object_Textblock_Format *fmt = ti->parent.format;
5176    int shad_sz = 0, shad_dst = 0, out_sz = 0;
5177    int dx = 0, minx = 0, maxx = 0, shx1, shx2;
5178    Evas_Object_Protected_Data *obj = c->evas_o;
5179    int l, r, t, b;
5180    l = r = t = b = 0;
5181 
5182    if (fmt->font.font)
5183      {
5184         ENFN->font_string_size_get(ENC, fmt->font.font,
5185                                    &ti->text_props, &tw, &th);
5186         advw = ENFN->font_h_advance_get(ENC, fmt->font.font,
5187                                         &ti->text_props);
5188      }
5189 
5190    if (EINA_UNLIKELY(ti->parent.format->gfx_filter != NULL))
5191      {
5192         Evas_Filter_Padding pad = { 0, 0, 0, 0 };
5193         Evas_Filter_Program *pgm;
5194 
5195         pgm = _format_filter_program_get(c->o, ti->parent.format);
5196         if (pgm)
5197           {
5198              evas_filter_program_padding_get(pgm, &pad, NULL);
5199 
5200              ti->parent.w = tw;                    // Don't add style pad here
5201              ti->parent.h = th;                    // Don't add style pad here
5202              ti->parent.adv = advw;
5203              ti->parent.x = 0;
5204              return;
5205           }
5206      }
5207 
5208    /* These adjustments are calculated and thus heavily linked to those in
5209     * textblock_render!!! Don't change one without the other. */
5210 
5211    switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC)
5212      {
5213       case EVAS_TEXT_STYLE_SHADOW:
5214         shad_dst = 1;
5215         break;
5216       case EVAS_TEXT_STYLE_OUTLINE_SHADOW:
5217       case EVAS_TEXT_STYLE_FAR_SHADOW:
5218         shad_dst = 2;
5219         out_sz = 1;
5220         break;
5221       case EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW:
5222         shad_dst = 1;
5223         shad_sz = 2;
5224         out_sz = 1;
5225         break;
5226       case EVAS_TEXT_STYLE_FAR_SOFT_SHADOW:
5227         shad_dst = 2;
5228         shad_sz = 2;
5229         break;
5230       case EVAS_TEXT_STYLE_SOFT_SHADOW:
5231         shad_dst = 1;
5232         shad_sz = 2;
5233         break;
5234       case EVAS_TEXT_STYLE_GLOW:
5235       case EVAS_TEXT_STYLE_SOFT_OUTLINE:
5236         out_sz = 2;
5237         break;
5238       case EVAS_TEXT_STYLE_OUTLINE:
5239         out_sz = 1;
5240         break;
5241       default:
5242         break;
5243      }
5244    switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_SHADOW_DIRECTION)
5245      {
5246       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT:
5247       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT:
5248       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT:
5249         dx = -1;
5250         break;
5251       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT:
5252       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT:
5253       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT:
5254         dx = 1;
5255         break;
5256       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP:
5257       case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM:
5258       default:
5259         dx = 0;
5260         break;
5261      }
5262    minx = -out_sz;
5263    maxx = out_sz;
5264    shx1 = dx * shad_dst;
5265    shx1 -= shad_sz;
5266    shx2 = dx * shad_dst;
5267    shx2 += shad_sz;
5268    if (shx1 < minx) minx = shx1;
5269    if (shx2 > maxx) maxx = shx2;
5270 
5271    ti->parent.w = tw;
5272    ti->parent.h = th;
5273    ti->parent.adv = advw;
5274    ti->parent.x = 0;
5275 
5276    l = -minx;
5277    r = maxx;
5278    // Get height padding as well
5279    evas_text_style_pad_get(fmt->style, NULL, NULL, &t, &b);
5280 
5281    if (l > c->style_pad.l) c->style_pad.l = l;
5282    if (r > c->style_pad.r) c->style_pad.r = r;
5283    if (t > c->style_pad.t) c->style_pad.t = t;
5284    if (b > c->style_pad.b) c->style_pad.b = b;
5285 }
5286 
5287 /**
5288  * @internal
5289  * Adds the item to the list, updates the item's properties (e.g, x,w,h)
5290  *
5291  * @param c the context
5292  * @param it the item itself.
5293  * @param rel item ti will be appended after, NULL = last.
5294  */
5295 static void
_layout_text_add_logical_item(Ctxt * c,Evas_Object_Textblock_Text_Item * ti,Eina_List * rel)5296 _layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Text_Item *ti,
5297       Eina_List *rel)
5298 {
5299    _text_item_update_sizes(c, ti);
5300 
5301    c->par->logical_items = eina_list_append_relative_list(
5302          c->par->logical_items, ti, rel);
5303 }
5304 
5305 static void
_layout_text_append_add_logical_item(Ctxt * c,Evas_Object_Textblock_Text_Item * ti,Eina_List * rel)5306 _layout_text_append_add_logical_item(Ctxt *c, Evas_Object_Textblock_Text_Item *ti,
5307       Eina_List *rel)
5308 {
5309    _text_item_update_sizes(c, ti);
5310 
5311    if (rel)
5312      {
5313         c->par->logical_items = eina_list_prepend_relative_list(
5314               c->par->logical_items, ti, rel);
5315      }
5316    else
5317      {
5318         c->par->logical_items = eina_list_append(
5319               c->par->logical_items, ti);
5320      }
5321 }
5322 
5323 typedef struct {
5324      EINA_INLIST;
5325      Evas_Object_Textblock_Format *format;
5326      size_t start;
5327      int off;
5328 } Layout_Text_Append_Queue;
5329 
5330 /**
5331  * @internal
5332  * Appends the text from node n starting at start ending at off to the layout.
5333  * It uses the fmt for the formatting.
5334  *
5335  * @param c the current context- NOT NULL.
5336  * @param fmt the format to use.
5337  * @param n the text node. - Not null.
5338  * @param start the start position. - in range.
5339  * @param off the offset - start + offset in range. if offset is -1, it'll add everything to the end of the string if offset = 0 it'll return with doing nothing.
5340  * @param repch a replacement char to print instead of the original string, for example, * when working with passwords.
5341  */
5342 static void
_layout_text_append(Ctxt * c,Layout_Text_Append_Queue * queue,Evas_Object_Textblock_Node_Text * n,int start,int off,const char * repch,Eina_List * rel)5343 _layout_text_append(Ctxt *c, Layout_Text_Append_Queue *queue, Evas_Object_Textblock_Node_Text *n, int start, int off, const char *repch, Eina_List *rel)
5344 {
5345    const Eina_Unicode *str = EINA_UNICODE_EMPTY_STRING;
5346    const Eina_Unicode *tbase;
5347    Evas_Object_Textblock_Text_Item *ti;
5348    size_t cur_len = 0;
5349    Eina_Unicode urepch = 0;
5350 
5351    /* prepare a working copy of the string, either filled by the repch or
5352     * filled with the true values */
5353    if (n)
5354      {
5355         int len;
5356         int orig_off = off;
5357 
5358         /* Figure out if we want to bail, work with an empty string,
5359          * or continue with a slice of the passed string */
5360         len = eina_ustrbuf_length_get(n->unicode);
5361         if (off == 0) return;
5362         else if (off < 0) off = len - start;
5363 
5364         if (start < 0)
5365           {
5366              start = 0;
5367           }
5368         else if ((start == 0) && (off == 0) && (orig_off == -1))
5369           {
5370              /* Special case that means that we need to add an empty
5371               * item */
5372              str = EINA_UNICODE_EMPTY_STRING;
5373              goto skip;
5374           }
5375         else if ((start >= len) || (start + off > len))
5376           {
5377              return;
5378           }
5379 
5380         /* If we work with a replacement char, create a string which is the same
5381          * but with replacement chars instead of regular chars. */
5382         if ((queue->format->password) && (repch) && (eina_ustrbuf_length_get(n->unicode)))
5383           {
5384              int i, ind;
5385              Eina_Unicode *ptr;
5386 
5387              tbase = str = ptr = alloca((off + 1) * sizeof(Eina_Unicode));
5388              ind = 0;
5389              urepch = eina_unicode_utf8_next_get(repch, &ind);
5390              for (i = 0 ; i < off; ptr++, i++)
5391                *ptr = urepch;
5392              *ptr = 0;
5393           }
5394         /* Use the string, just cut the relevant parts */
5395         else
5396           {
5397              str = eina_ustrbuf_string_get(n->unicode) + start;
5398           }
5399 
5400         cur_len = off;
5401      }
5402 
5403 skip:
5404    tbase = str;
5405 
5406    /* If there's no parent text node, only create an empty item */
5407    if (!n)
5408      {
5409         ti = _layout_text_item_new(c, queue->format);
5410         ti->parent.text_node = NULL;
5411         ti->parent.text_pos = 0;
5412         _layout_text_append_add_logical_item(c, ti, rel);
5413 
5414         return;
5415      }
5416 
5417    while (cur_len > 0)
5418      {
5419         Evas_Font_Instance *script_fi = NULL;
5420         int script_len, tmp_cut;
5421         Evas_Script_Type script;
5422 
5423         script_len = cur_len;
5424 
5425         tmp_cut = evas_common_language_script_end_of_run_get(str,
5426               c->par->bidi_props, start + str - tbase, script_len);
5427         if (tmp_cut > 0)
5428           {
5429              script_len = tmp_cut;
5430           }
5431         cur_len -= script_len;
5432 
5433         script = evas_common_language_script_type_get(str, script_len);
5434 
5435         /* FIXME Workaround for Burmese Vowel E Rendering, caused by bug in Harfbuzz
5436            breaking text run will fix the visual issue.
5437         */
5438         if (script == EVAS_SCRIPT_MYANMAR && script_len > 1)
5439           {
5440              int i;
5441              for (i = 0 ; i < script_len - 1; i++)
5442                {
5443                   if (str[i] == 0x200C)
5444                     {
5445                        if (str[i+1] == 0x1031)
5446                          {
5447                             cur_len += script_len;
5448                             script_len = i + 1;
5449                             cur_len -= script_len;
5450                             break;
5451                          }
5452                     }
5453                }
5454           }
5455 
5456         Evas_Object_Protected_Data *obj = efl_data_scope_get(c->obj, EFL_CANVAS_OBJECT_CLASS);
5457         while (script_len > 0)
5458           {
5459              Evas_Font_Instance *cur_fi = NULL;
5460              size_t run_start;
5461              int run_len = script_len;
5462              ti = _layout_text_item_new(c, queue->format);
5463              ti->parent.text_node = n;
5464              ti->parent.text_pos = run_start = start + str - tbase;
5465 
5466              if (ti->parent.format->font.font)
5467                {
5468                   run_len = ENFN->font_run_end_get(ENC,
5469                         ti->parent.format->font.font, &script_fi, &cur_fi,
5470                         script, str, script_len);
5471                }
5472 
5473              evas_common_text_props_bidi_set(&ti->text_props,
5474                    c->par->bidi_props, ti->parent.text_pos);
5475              evas_common_text_props_script_set(&ti->text_props, script);
5476 
5477              if (cur_fi)
5478                {
5479                   ENFN->font_text_props_info_create(ENC,
5480                         cur_fi, str, &ti->text_props, c->par->bidi_props,
5481                         ti->parent.text_pos, run_len, EVAS_TEXT_PROPS_MODE_SHAPE,
5482                         ti->parent.format->font.fdesc->lang);
5483                }
5484 
5485              while ((queue->start + queue->off) < (run_start + run_len))
5486                {
5487                   Evas_Object_Textblock_Text_Item *new_ti;
5488 
5489                   /* There must be a next because of the test in the while. */
5490                   queue = (Layout_Text_Append_Queue *) EINA_INLIST_GET(queue)->next;
5491 
5492                   new_ti = _layout_text_item_new(c, queue->format);
5493                   new_ti->parent.text_node = ti->parent.text_node;
5494                   new_ti->parent.text_pos = queue->start;
5495 
5496                   evas_common_text_props_split(&ti->text_props, &new_ti->text_props,
5497                         new_ti->parent.text_pos - ti->parent.text_pos);
5498 
5499                   _layout_text_append_add_logical_item(c, ti, rel);
5500                   ti = new_ti;
5501                }
5502 
5503              _layout_text_append_add_logical_item(c, ti, rel);
5504 
5505              str += run_len;
5506              script_len -= run_len;
5507           }
5508      }
5509 }
5510 
5511 /**
5512  * @internal
5513  * Add a format item from the format node n and the item item.
5514  *
5515  * @param c the current context- NOT NULL.
5516  * @param n the source format node - not null.
5517  * @param item the format text.
5518  *
5519  * @return the new format item.
5520  */
5521 static Evas_Object_Textblock_Format_Item *
_layout_format_item_add(Ctxt * c,Evas_Object_Textblock_Node_Format * n,const char * item,Evas_Object_Textblock_Format * fmt)5522 _layout_format_item_add(Ctxt *c, Evas_Object_Textblock_Node_Format *n, const char *item, Evas_Object_Textblock_Format *fmt)
5523 {
5524    Evas_Object_Textblock_Format_Item *fi;
5525 
5526    fi = calloc(1, sizeof(Evas_Object_Textblock_Format_Item));
5527    fi->item = eina_stringshare_add(item);
5528    fi->parent.type = EVAS_TEXTBLOCK_ITEM_FORMAT;
5529    fi->parent.format = fmt;
5530    fi->parent.format->ref++;
5531    c->par->logical_items = eina_list_append(c->par->logical_items, fi);
5532    if (n)
5533      {
5534         fi->parent.text_node = n->text_node;
5535         /* FIXME: make it more efficient */
5536         fi->parent.text_pos = _evas_textblock_node_format_pos_get(n);
5537 #ifdef BIDI_SUPPORT
5538         fi->bidi_dir = (evas_bidi_is_rtl_char(
5539               c->par->bidi_props,
5540               0,
5541               fi->parent.text_pos)) ?
5542            EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
5543 #else
5544         fi->bidi_dir = EVAS_BIDI_DIRECTION_LTR;
5545 #endif
5546      }
5547    return fi;
5548 }
5549 
5550 /**
5551  * @internal
5552  * Should be call after we finish filling a format.
5553  * FIXME: doc.
5554  */
5555 static void
_format_finalize(Evas_Object * eo_obj,Evas_Object_Textblock_Format * fmt)5556 _format_finalize(Evas_Object *eo_obj, Evas_Object_Textblock_Format *fmt)
5557 {
5558    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
5559    void *of;
5560 
5561    of = fmt->font.font;
5562 
5563    fmt->font.font = evas_font_load(obj->layer->evas->font_path,
5564                                    obj->layer->evas->hinting,
5565                                    fmt->font.fdesc,
5566                                    fmt->font.source,
5567                                    (int)(((double) fmt->font.size) * obj->cur->scale),
5568                                    fmt->font.bitmap_scalable);
5569    evas_font_free(of);
5570 }
5571 
5572 static Efl_Canvas_Textblock_Filter_Program *
_filter_program_find(Efl_Canvas_Textblock_Data * o,const char * name)5573 _filter_program_find(Efl_Canvas_Textblock_Data *o, const char *name)
5574 {
5575    Efl_Canvas_Textblock_Filter_Program *prg;
5576 
5577    if (!name) return NULL;
5578    EINA_INLIST_FOREACH(o->gfx_filter.programs, prg)
5579      {
5580         if (eina_streq(name, prg->name))
5581           return prg;
5582      }
5583 
5584    return NULL;
5585 }
5586 
5587 static Evas_Filter_Program *
_format_filter_program_get(Efl_Canvas_Textblock_Data * o,Evas_Object_Textblock_Format * fmt)5588 _format_filter_program_get(Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Format *fmt)
5589 {
5590    Efl_Canvas_Textblock_Filter_Program *program;
5591    Efl_Canvas_Textblock_Filter *filter;
5592    Evas_Filter_Program *pgm;
5593 
5594    filter = fmt->gfx_filter;
5595    if (!filter) return NULL;
5596 
5597    program = _filter_program_find(o, filter->name);
5598    if (!program) return NULL;
5599 
5600    if (program->changed)
5601      {
5602         evas_filter_program_del(program->pgm);
5603         program->pgm = NULL;
5604      }
5605    pgm = program->pgm;
5606    if (!pgm)
5607      {
5608         pgm = evas_filter_program_new(program->name, EINA_FALSE);
5609         evas_filter_program_data_set_all(pgm, EINA_INLIST_GET(o->gfx_filter.data_bindings));
5610         evas_filter_program_source_set_all(pgm, o->gfx_filter.sources);
5611         if (!evas_filter_program_parse(pgm, program->code))
5612           {
5613              evas_filter_program_del(pgm);
5614              filter->invalid = EINA_TRUE;
5615              return NULL;
5616           }
5617         filter->invalid = EINA_FALSE;
5618         program->pgm = pgm;
5619      }
5620 
5621    return pgm;
5622 }
5623 
5624 /**
5625  * @internal
5626  * Returns true if the item is a tab
5627  * @def _IS_TAB(item)
5628  */
5629 #define _IS_TAB(item)                                             \
5630    (!strcmp(item, "tab") || !strcmp(item, "\t") || !strcmp(item, "\\t"))
5631 /**
5632  * @internal
5633  * Returns true if the item is a line spearator, false otherwise
5634  * @def _IS_LINE_SEPARATOR(item)
5635  */
5636 #define _IS_LINE_SEPARATOR(item)                                             \
5637    (!strcmp(item, "br") || !strcmp(item, "\n") || !strcmp(item, "\\n"))
5638 /**
5639  * @internal
5640  * Returns true if the item is a paragraph separator, false otherwise
5641  * @def _IS_PARAGRAPH_SEPARATOR(item)
5642  */
5643 #define _IS_PARAGRAPH_SEPARATOR_SIMPLE(item)                                 \
5644    (!strcmp(item, "ps"))
5645 /**
5646  * @internal
5647  * Returns true if the item is a paragraph separator, false otherwise
5648  * takes legacy mode into account.
5649  * @def _IS_PARAGRAPH_SEPARATOR(item)
5650  */
5651 #define _IS_PARAGRAPH_SEPARATOR(o, item)                                     \
5652    (_IS_PARAGRAPH_SEPARATOR_SIMPLE(item) ||                                  \
5653     (o->legacy_newline && _IS_LINE_SEPARATOR(item))) /* Paragraph separator */
5654 
5655 /**
5656  * @internal
5657  * Handles a format by processing a format node. It returns the relevant format
5658  * through _fmt and updates the padding through style_pad_*. If needed,
5659  * it creates a format item.
5660  *
5661  * @param obj the evas object - NOT NULL.
5662  * @param c the current context- NOT NULL.
5663  * @param _fmt the format that holds the result.
5664  * @param n the source format node - not null.
5665  * @param style_pad_l the pad to update.
5666  * @param style_pad_r the pad to update.
5667  * @param style_pad_t the pad to update.
5668  * @param style_pad_b the pad to update.
5669  * @param create_item Create a new format item if true, only process otherwise.
5670  *
5671  * @return fi if created.
5672  */
5673 static Evas_Object_Textblock_Format_Item *
_layout_do_format(const Evas_Object * obj,Ctxt * c,Evas_Object_Textblock_Format ** _fmt,Evas_Object_Textblock_Node_Format * n,int * style_pad_l,int * style_pad_r,int * style_pad_t,int * style_pad_b,Eina_Bool create_item)5674 _layout_do_format(const Evas_Object *obj, Ctxt *c,
5675       Evas_Object_Textblock_Format **_fmt, Evas_Object_Textblock_Node_Format *n,
5676       int *style_pad_l, int *style_pad_r, int *style_pad_t, int *style_pad_b,
5677       Eina_Bool create_item)
5678 {
5679    Evas_Object_Textblock_Format_Item *fi = NULL;
5680    Evas_Object_Textblock_Format *fmt = *_fmt;
5681    /* FIXME: comment the algo */
5682 
5683    const char *s;
5684    const char *item;
5685    int handled = 0;
5686    Eina_Bool is_item = (n->annotation && n->annotation->is_item && n->opener);
5687 
5688    s = n->format;
5689    if (!strncmp(s, "item ", 5) || is_item)
5690      {
5691         // one of:
5692         //   item size=20x10 href=name
5693         //   item relsize=20x10 href=name
5694         //   item abssize=20x10 href=name
5695         //
5696         // optional arguments:
5697         //   vsize=full
5698         //   vsize=ascent
5699         //
5700         // size == item size (modifies line size) - can be multiplied by
5701         //   scale factor
5702         // relsize == relative size (height is current font height, width
5703         //   modified accordingly keeping aspect)
5704         // abssize == absolute size (modifies line size) - never multiplied by
5705         //   scale factor
5706         // href == name of item - to be found and matched later and used for
5707         //   positioning
5708         int w = 1, h = 1;
5709         int vsize = 0, size = 0;
5710         char *p;
5711 
5712         // don't care
5713         //href = strstr(s, " href=");
5714         p = strstr(s, is_item ? "vsize=" : " vsize=");
5715         if (p)
5716           {
5717              p += (is_item ? 6 : 7);
5718              if (!strncmp(p, "full", 4)) vsize = VSIZE_FULL;
5719              else if (!strncmp(p, "ascent", 6)) vsize = VSIZE_ASCENT;
5720           }
5721         p = strstr(s, is_item ? "size=" : " size=");
5722         if (p)
5723           {
5724              p += (is_item ? 5 : 6);
5725              if (sscanf(p, "%ix%i", &w, &h) == 2)
5726                {
5727                   /* this is handled somewhere else because it depends
5728                    * on the current scaling factor of the object which
5729                    * may change and break because the results of this
5730                    * function are cached */
5731                   size = SIZE;
5732                }
5733           }
5734         else
5735           {
5736              p = strstr(s, is_item ? "absize=" : " absize=");
5737              if (p)
5738                {
5739                   p += (is_item ? 7 : 8);
5740                   if (sscanf(p, "%ix%i", &w, &h) == 2)
5741                     {
5742                        size = SIZE_ABS;
5743                     }
5744                }
5745              else
5746                {
5747                   p = strstr(s, is_item ? "relsize=" : " relsize=");
5748                   if (p)
5749                     {
5750                        /* this is handled somewhere else because it depends
5751                         * on the line it resides in, which is not defined
5752                         * at this point and will change anyway, which will
5753                         * break because the results of this function are
5754                         * cached */
5755                        size = SIZE_REL;
5756                     }
5757                }
5758           }
5759 
5760         if (create_item)
5761           {
5762              fi = _layout_format_item_add(c, n, s, fmt);
5763              fi->vsize = vsize;
5764              fi->size = size;
5765              fi->formatme = 1;
5766              /* For formats items it's usually
5767                 the same, we don't handle the
5768                 special cases yet. */
5769              fi->parent.w = fi->parent.adv = w;
5770              fi->parent.h = h;
5771           }
5772         /* Not sure if it's the best handling, but will do it for now. */
5773         fmt = _layout_format_push(c, fmt, n);
5774         handled = 1;
5775      }
5776 
5777    if (!handled)
5778      {
5779         Eina_Bool push_fmt = EINA_FALSE;
5780         if (n->opener && !n->own_closer)
5781           {
5782              fmt = _layout_format_push(c, fmt, n);
5783              push_fmt = EINA_TRUE;
5784           }
5785         else if (!n->opener)
5786           {
5787              fmt = _layout_format_pop(c, n->orig_format);
5788           }
5789         while ((item = _format_parse(&s, EINA_FALSE)))
5790           {
5791              if (_format_is_param(item))
5792                {
5793                   /* Only handle it if it's a push format, otherwise,
5794                    * don't let overwrite the format stack.. */
5795                   if (push_fmt)
5796                     {
5797                        _layout_format_value_handle(c, fmt, item);
5798                     }
5799                }
5800              else if (create_item)
5801                {
5802                   if ((_IS_PARAGRAPH_SEPARATOR(c->o, item)) ||
5803                         (_IS_LINE_SEPARATOR(item)))
5804                     {
5805                        fi = _layout_format_item_add(c, n, item, fmt);
5806 
5807                        fi->parent.w = fi->parent.adv = 0;
5808                     }
5809                   else if (_IS_TAB(item))
5810                     {
5811                        fi = _layout_format_item_add(c, n, item, fmt);
5812                        fi->parent.w = fi->parent.adv = fmt->tabstops;
5813                        fi->formatme = 1;
5814                     }
5815                }
5816           }
5817         _format_finalize(c->obj, fmt);
5818      }
5819 
5820      {
5821         Evas_Filter_Padding pad = { 0, 0, 0, 0 };
5822         Evas_Filter_Program *pgm = NULL;
5823 
5824         if (EINA_UNLIKELY(fmt->gfx_filter != NULL))
5825           pgm = _format_filter_program_get(efl_data_scope_get(obj, MY_CLASS), fmt);
5826 
5827         if (EINA_UNLIKELY(pgm != NULL))
5828           evas_filter_program_padding_get(pgm, &pad, NULL);
5829         else
5830           evas_text_style_pad_get(fmt->style, &pad.l, &pad.r, &pad.t, &pad.b);
5831 
5832         if (pad.l > *style_pad_l) *style_pad_l = pad.l;
5833         if (pad.r > *style_pad_r) *style_pad_r = pad.r;
5834         if (pad.t > *style_pad_t) *style_pad_t = pad.t;
5835         if (pad.b > *style_pad_b) *style_pad_b = pad.b;
5836      }
5837 
5838    if (fmt->underline2)
5839      c->have_underline2 = 1;
5840    else if (fmt->underline || fmt->underline_dash)
5841      c->have_underline = 1;
5842    *_fmt = fmt;
5843 
5844    return fi;
5845 }
5846 
5847 static void
_layout_update_par(Ctxt * c)5848 _layout_update_par(Ctxt *c)
5849 {
5850    Evas_Object_Textblock_Paragraph *last_par;
5851    last_par = (Evas_Object_Textblock_Paragraph *)
5852       EINA_INLIST_GET(c->par)->prev;
5853    if (last_par)
5854      {
5855         c->par->y = last_par->y + last_par->h;
5856      }
5857    else
5858      {
5859         c->par->y = 0;
5860      }
5861 }
5862 
5863 /* -1 means no wrap */
5864 static int
_layout_get_charwrap(Ctxt * c,Evas_Object_Textblock_Format * fmt,const Evas_Object_Textblock_Item * it,size_t line_start,const char * breaks,Evas_Coord w,Evas_Coord from_x)5865 _layout_get_charwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
5866       const Evas_Object_Textblock_Item *it, size_t line_start,
5867       const char *breaks,
5868       Evas_Coord w, Evas_Coord from_x)
5869 {
5870    int wrap;
5871    size_t uwrap;
5872    size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
5873    /* Currently not being used, because it doesn't contain relevant
5874     * information */
5875    (void) breaks;
5876 
5877      {
5878         if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
5879            wrap = 0;
5880         else
5881            wrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it),
5882                 w, from_x, 0);
5883 
5884         if (wrap < 0)
5885            return -1;
5886         uwrap = (size_t) wrap + it->text_pos;
5887      }
5888 
5889 
5890    if ((uwrap == line_start) && (it->type == EVAS_TEXTBLOCK_ITEM_TEXT))
5891      {
5892         uwrap = it->text_pos +
5893            (size_t) evas_common_text_props_cluster_next(
5894                  &_ITEM_TEXT(it)->text_props, wrap);
5895      }
5896    if ((uwrap <= line_start) || (uwrap > len))
5897       return -1;
5898 
5899    return uwrap;
5900 }
5901 
5902 /* -1 means no wrap */
5903 /* Allow break means: if we can break after the current char */
5904 #define ALLOW_BREAK(i) \
5905    (breaks[i] <= LINEBREAK_ALLOWBREAK)
5906 
5907 /* Give a position in text, find the end of word by using Unicode word
5908  * boundary rules */
5909 static inline size_t
_layout_word_end(const char * breaks,size_t pos,size_t len)5910 _layout_word_end(const char *breaks, size_t pos, size_t len)
5911 {
5912    for ( ; (pos < len - 1) && (breaks[pos] != WORDBREAK_BREAK) ; pos++)
5913       ;
5914    return pos;
5915 }
5916 
5917 #define SHY_HYPHEN 0xad
5918 
5919 static int
_layout_get_hyphenationwrap(Ctxt * c,Evas_Object_Textblock_Format * fmt,const Evas_Object_Textblock_Item * it,size_t line_start,const char * breaks,const char * wordbreaks,Evas_Coord w,Evas_Coord from_x)5920 _layout_get_hyphenationwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
5921       const Evas_Object_Textblock_Item *it, size_t line_start,
5922       const char *breaks, const char *wordbreaks,
5923       Evas_Coord w, Evas_Coord from_x)
5924 {
5925    size_t wrap;
5926    size_t orig_wrap;
5927    const Eina_Unicode *str = eina_ustrbuf_string_get(
5928          it->text_node->unicode);
5929    int item_start = it->text_pos;
5930    size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
5931    Eina_Bool try_hyphenate = EINA_FALSE;
5932 
5933      {
5934         int swrap = -1;
5935         int hyphen_swrap = -1;
5936 
5937         if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
5938            swrap = 0;
5939         else
5940           {
5941              /* Get cutoff */
5942              swrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it), 0,
5943                    w, from_x);
5944 
5945              /* Get cutoff considering an additional hyphen item */
5946              c->hyphen_ti = _layout_hyphen_item_new(c, _ITEM_TEXT(it));
5947              hyphen_swrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it),
5948                   w - c->hyphen_ti->parent.w, from_x, c->hyphen_ti->parent.w);
5949 
5950              /* Stronger condition than '< 0' for hyphenations */
5951              if (hyphen_swrap >= 2)
5952                {
5953                   try_hyphenate = EINA_TRUE;
5954                }
5955              else
5956                {
5957                   _item_free(c->o, c->evas_o, NULL, _ITEM(c->hyphen_ti));
5958                   c->hyphen_ti = NULL;
5959                }
5960           }
5961 
5962         if (swrap < 0)
5963            return -1;
5964 
5965         orig_wrap = wrap = swrap + item_start;
5966         if (try_hyphenate)
5967           {
5968              orig_wrap = wrap = hyphen_swrap + item_start;
5969           }
5970      }
5971 
5972    if (wrap > line_start)
5973      {
5974         Eina_Bool found_hyphen = EINA_FALSE;
5975         size_t word_end;
5976 
5977         if (!_is_white(str[wrap]) || (wrap + 1 == len))
5978            MOVE_PREV_UNTIL(line_start, wrap);
5979 
5980         /* If there's a breakable point inside the text, scan backwards until
5981          * we find it */
5982         while (wrap > line_start)
5983           {
5984              /* When iterating back, 'wrap - 1' is the word delimiter,
5985               * but isn't the word's start. The word's start is 'wrap'. */
5986              if (try_hyphenate && ((wordbreaks[wrap - 1] == WORDBREAK_BREAK) ||
5987                       (wrap - 1 == line_start)))
5988                {
5989                   size_t word_start, word_len;
5990 
5991                   word_start = wrap; /* easier to understand if we tag this */
5992                   word_end = _layout_word_end(wordbreaks, wrap, len);
5993                   word_len = word_end - word_start + 1;
5994 
5995                   if (word_len >= 4)
5996                     {
5997                        size_t hyphen_off;
5998                        size_t i = 0;
5999                        size_t pos = 0;
6000 
6001 #ifdef HAVE_HYPHEN
6002                        char *hyphens = _layout_wrap_hyphens_get(
6003                              str, it->format->font.fdesc->lang,
6004                              word_start, word_len);
6005 #endif
6006 
6007                        /* This only happens one time, if the cutoff is in
6008                         * the middle of this word */
6009                        if (word_end > orig_wrap - 1)
6010                          {
6011                             word_end = orig_wrap - 1;
6012                          }
6013 
6014                        hyphen_off = word_end - word_start;
6015 
6016                        /* We limit our search to the start of the line */
6017                        if (word_start < line_start)
6018                          {
6019                             word_start = line_start;
6020                          }
6021 
6022                        for (i = hyphen_off, pos = word_end ; pos > word_start ; i--, pos--)
6023                          {
6024                             if (
6025 #ifdef HAVE_HYPHEN
6026                                   (hyphens && (hyphens[i] & 1)) ||
6027 #endif
6028                                   (str[pos] == SHY_HYPHEN))
6029                               {
6030                                  found_hyphen = EINA_TRUE;
6031                                  break;
6032                               }
6033                          }
6034 
6035 #ifdef HAVE_HYPHEN
6036                        if (hyphens)
6037                          {
6038                             free(hyphens);
6039                          }
6040 #endif
6041 
6042                        /* Rejecting sequences smaller than 2 characters.
6043                         * This also works with 'i' initialized to 0 */
6044                        if (found_hyphen)
6045                          {
6046                             wrap = pos;
6047                             break;
6048                          }
6049                     }
6050                }
6051 
6052              /* SHY-HYPHEN is considered a wordbreak. We don't block it
6053               * internally in ALLOW_BREAK, just here. */
6054              if (ALLOW_BREAK(wrap) && (str[wrap] != SHY_HYPHEN))
6055                 break;
6056              wrap--;
6057           }
6058 
6059         /* hyphen item cleanup */
6060         if (!found_hyphen && c->hyphen_ti)
6061           {
6062              _item_free(c->o, c->evas_o, NULL, _ITEM(c->hyphen_ti));
6063              c->hyphen_ti = NULL;
6064           }
6065 
6066         if ((wrap > line_start) ||
6067               ((wrap == line_start) && (ALLOW_BREAK(wrap)) && (wrap < len)))
6068           {
6069              /* We found a suitable wrapping point, break here. */
6070              MOVE_NEXT_UNTIL(len, wrap);
6071              return wrap;
6072           }
6073      }
6074 
6075    /* Hyphenation falls-back to char wrapping at start of line */
6076    return _layout_get_charwrap(c, fmt, it,
6077          line_start, breaks, w, from_x);
6078 }
6079 
6080 static int
_layout_get_word_mixwrap_common(Ctxt * c,Evas_Object_Textblock_Format * fmt,const Evas_Object_Textblock_Item * it,Eina_Bool mixed_wrap,size_t line_start,const char * breaks,Evas_Coord w,Evas_Coord from_x,Eina_Bool scan_fwd)6081 _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
6082       const Evas_Object_Textblock_Item *it, Eina_Bool mixed_wrap,
6083       size_t line_start, const char *breaks,
6084       Evas_Coord w, Evas_Coord from_x,
6085       Eina_Bool scan_fwd)
6086 {
6087    Eina_Bool wrap_after = EINA_FALSE;
6088    size_t wrap;
6089    size_t orig_wrap;
6090    const Eina_Unicode *str = eina_ustrbuf_string_get(
6091          it->text_node->unicode);
6092    int item_start = it->text_pos;
6093    size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
6094 
6095      {
6096         int swrap = -1;
6097         if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
6098            swrap = 0;
6099         else
6100            swrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it), w, from_x, 0);
6101         /* Avoiding too small textblocks to even contain one char.
6102          * FIXME: This can cause breaking inside ligatures. */
6103 
6104         if (swrap < 0)
6105            return -1;
6106 
6107         orig_wrap = wrap = swrap + item_start;
6108      }
6109 
6110    if (wrap > line_start)
6111      {
6112         /* The wrapping point found is the first char of the next string
6113            the rest works on the last char of the previous string.
6114            If it's a whitespace, then it's ok, and no need to go back
6115            because we'll remove it anyway. */
6116         if (!_is_white(str[wrap]) || (wrap + 1 == len))
6117            MOVE_PREV_UNTIL(line_start, wrap);
6118         /* If there's a breakable point inside the text, scan backwards until
6119          * we find it */
6120         while (wrap > line_start)
6121           {
6122              if (ALLOW_BREAK(wrap))
6123                 break;
6124              wrap--;
6125           }
6126 
6127         if ((wrap > line_start) ||
6128               ((wrap == line_start) && (ALLOW_BREAK(wrap)) && (wrap < len)))
6129           {
6130              /* We found a suitable wrapping point, break here. */
6131              MOVE_NEXT_UNTIL(len, wrap);
6132              return wrap;
6133           }
6134         else
6135           {
6136              if (mixed_wrap)
6137                {
6138                   return ((orig_wrap >= line_start) && (orig_wrap < len)) ?
6139                      ((int) orig_wrap) : -1;
6140                }
6141              else if (scan_fwd)
6142                {
6143                   /* Scan forward to find the next wrapping point */
6144                   wrap = orig_wrap;
6145                   wrap_after = EINA_TRUE;
6146                }
6147           }
6148      }
6149 
6150    /* If we need to find the position after the cutting point */
6151    if ((wrap == line_start) || (wrap_after))
6152      {
6153         if (!scan_fwd) return wrap;
6154         if (mixed_wrap)
6155           {
6156              return _layout_get_charwrap(c, fmt, it,
6157                    line_start, breaks, w, from_x);
6158           }
6159         else
6160           {
6161              while (wrap < len)
6162                {
6163                   if (ALLOW_BREAK(wrap))
6164                      break;
6165                   wrap++;
6166                }
6167 
6168 
6169              if ((wrap < len) && (wrap >= line_start))
6170                {
6171                   MOVE_NEXT_UNTIL(len, wrap);
6172                   return wrap;
6173                }
6174              else
6175                {
6176                   return -1;
6177                }
6178           }
6179      }
6180 
6181    return -1;
6182 }
6183 
6184 /* -1 means no wrap */
6185 static int
_layout_get_wordwrap(Ctxt * c,Evas_Object_Textblock_Format * fmt,const Evas_Object_Textblock_Item * it,size_t line_start,const char * breaks,Evas_Coord w,Evas_Coord from_x,Eina_Bool allow_scan_fwd)6186 _layout_get_wordwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
6187       const Evas_Object_Textblock_Item *it, size_t line_start,
6188       const char *breaks,
6189       Evas_Coord w, Evas_Coord from_x,
6190       Eina_Bool allow_scan_fwd)
6191 {
6192    return _layout_get_word_mixwrap_common(c, fmt, it, EINA_FALSE, line_start,
6193          breaks, w, from_x, allow_scan_fwd);
6194 }
6195 
6196 /* -1 means no wrap */
6197 static int
_layout_get_mixedwrap(Ctxt * c,Evas_Object_Textblock_Format * fmt,const Evas_Object_Textblock_Item * it,size_t line_start,const char * breaks,Evas_Coord w,Evas_Coord from_x,Eina_Bool allow_scan_fwd)6198 _layout_get_mixedwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
6199       const Evas_Object_Textblock_Item *it, size_t line_start,
6200       const char *breaks,
6201       Evas_Coord w, Evas_Coord from_x,
6202       Eina_Bool allow_scan_fwd)
6203 {
6204    return _layout_get_word_mixwrap_common(c, fmt, it, EINA_TRUE, line_start,
6205          breaks, w, from_x, allow_scan_fwd);
6206 }
6207 
6208 static Evas_Object_Textblock_Text_Item *
_layout_ellipsis_item_new(Ctxt * c,const Evas_Object_Textblock_Item * cur_it)6209 _layout_ellipsis_item_new(Ctxt *c, const Evas_Object_Textblock_Item *cur_it)
6210 {
6211    const Eina_Unicode _ellip_str[2] = { 0x2026, '\0' };
6212    Evas_Object_Textblock_Text_Item *ellip_ti;
6213    Evas_Script_Type script;
6214    Evas_Font_Instance *script_fi = NULL, *cur_fi;
6215    size_t len = 1; /* The length of _ellip_str */
6216 
6217    /* We can free it here, cause there's only one ellipsis item per tb. */
6218    if (c->o->ellip_ti) _item_free(c->o, c->evas_o, NULL, _ITEM(c->o->ellip_ti));
6219    c->o->ellip_ti = ellip_ti = _layout_text_item_new(c, cur_it->format);
6220    ellip_ti->parent.text_node = cur_it->text_node;
6221    ellip_ti->parent.text_pos = cur_it->text_pos;
6222    script = evas_common_language_script_type_get(_ellip_str, len);
6223 
6224    evas_common_text_props_bidi_set(&ellip_ti->text_props,
6225          c->par->bidi_props, ellip_ti->parent.text_pos);
6226    evas_common_text_props_script_set (&ellip_ti->text_props, script);
6227 
6228    if (ellip_ti->parent.format->font.font)
6229      {
6230         Evas_Object_Protected_Data *obj = c->evas_o;
6231         /* It's only 1 char anyway, we don't need the run end. */
6232         (void) ENFN->font_run_end_get(ENC,
6233               ellip_ti->parent.format->font.font, &script_fi, &cur_fi,
6234               script, _ellip_str, len);
6235 
6236         ENFN->font_text_props_info_create(ENC,
6237               cur_fi, _ellip_str, &ellip_ti->text_props,
6238               c->par->bidi_props, ellip_ti->parent.text_pos, len, EVAS_TEXT_PROPS_MODE_SHAPE,
6239               ellip_ti->parent.format->font.fdesc->lang);
6240      }
6241 
6242    _text_item_update_sizes(c, ellip_ti);
6243 
6244    if (cur_it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
6245      {
6246         ellip_ti->parent.text_pos += _ITEM_TEXT(cur_it)->text_props.text_len
6247            - 1;
6248      }
6249    else
6250      {
6251         ellip_ti->parent.text_pos++;
6252      }
6253 
6254    return ellip_ti;
6255 }
6256 
6257 /**
6258  * @internel
6259  * Handle ellipsis
6260  */
6261 static inline void
_layout_handle_ellipsis(Ctxt * c,Evas_Object_Textblock_Item * it,Eina_List * i)6262 _layout_handle_ellipsis(Ctxt *c, Evas_Object_Textblock_Item *it, Eina_List *i)
6263 {
6264    Evas_Object_Textblock_Text_Item *ti, *ellip_ti;
6265    Evas_Object_Textblock_Item *last_it, *prev_it;
6266    Evas_Coord save_cx, ellip_w;
6267    Evas_Coord temp_w;
6268    int wrap;
6269    ellip_ti = _layout_ellipsis_item_new(c, it);
6270    prev_it = last_it = it;
6271 
6272    save_cx = c->x;
6273    temp_w = c->w;
6274    ellip_w = ellip_ti->parent.w;
6275 #ifdef BIDI_SUPPORT
6276    // XXX: with RTL considerations in mind, we need to take max(adv, w) as the
6277    // line may be reordered in a way that the item placement will cause the
6278    // formatted width to exceed the width constraints.
6279    if (c->par->is_bidi && ellip_ti->parent.adv > ellip_ti->parent.w)
6280      {
6281         ellip_w = ellip_ti->parent.adv;
6282      }
6283 #endif
6284    temp_w -= ellip_w;
6285 
6286    /* If there is no enough space for ellipsis item, remove all of items */
6287    if (temp_w <= 0)
6288      {
6289         while (c->ln->items)
6290           {
6291              last_it = _ITEM(EINA_INLIST_GET(c->ln->items)->last);
6292              c->ln->items = _ITEM(eina_inlist_remove(
6293                    EINA_INLIST_GET(c->ln->items),
6294                    EINA_INLIST_GET(last_it)));
6295           }
6296         last_it = NULL;
6297      }
6298 
6299    while (last_it)
6300      {
6301         if (last_it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
6302           {
6303              ti = _ITEM_TEXT(last_it);
6304 
6305              wrap = _layout_text_cutoff_get(c, last_it->format, ti,
6306                   temp_w, c->x, ellip_ti->parent.w);
6307 
6308              if ((wrap > 0) && !IS_AT_END(ti, (size_t) wrap))
6309                {
6310                   Evas_Object_Textblock_Text_Item *new_ti;
6311                   Eina_List *l = i;
6312 
6313                   while (l)
6314                     {
6315                        Evas_Object_Textblock_Item *iit = eina_list_data_get(l);
6316                        if (iit == _ITEM(ti)) break;
6317                        l = eina_list_prev(l);
6318                     }
6319                   new_ti = _layout_item_text_split_strip_white(c, ti, l, wrap);
6320                   ellip_ti->parent.text_pos = new_ti->parent.text_pos;
6321                   break;
6322                }
6323              else if (wrap < 0)
6324                {
6325                   // Removal of the previous item left enough space.
6326                   ellip_ti->parent.text_pos = prev_it->text_pos;
6327                   break;
6328                }
6329           }
6330         else
6331           {
6332              /* We will ignore format items. ex) tab
6333               * But, if there is <item> tag and size is acceptable, we have to insert it to line. */
6334              if (!strncmp(_ITEM_FORMAT(last_it)->item, "item", 4) &&
6335                  ((temp_w - c->o->style_pad.l - c->o->style_pad.r - c->marginl - c->marginr) >= (c->x + last_it->adv)))
6336                {
6337                   break;
6338                }
6339           }
6340 
6341         if (c->ln->items && last_it != it)
6342           {
6343              c->ln->items = _ITEM(eina_inlist_remove(
6344                    EINA_INLIST_GET(c->ln->items),
6345                    EINA_INLIST_GET(last_it)));
6346           }
6347 
6348         prev_it = last_it;
6349         last_it = (c->ln->items) ? _ITEM(EINA_INLIST_GET(c->ln->items)->last) : NULL;
6350 
6351         if (last_it)
6352           {
6353              /* We need to renew ellipsis item.
6354               * Because, base format is changed to last_it.
6355               * We can't reuse it. */
6356              temp_w += ellip_ti->parent.w;
6357              ellip_ti = _layout_ellipsis_item_new(c, last_it);
6358              temp_w -= ellip_ti->parent.w;
6359              c->x -= last_it->adv;
6360              if (c->x < 0)
6361                c->x = 0;
6362              save_cx = c->x;
6363           }
6364      }
6365 
6366    c->x = save_cx;
6367    /* If we should add this item, do it */
6368    if (last_it == it)
6369      {
6370         c->ln->items = (Evas_Object_Textblock_Item *)
6371            eina_inlist_append(EINA_INLIST_GET(c->ln->items),
6372                  EINA_INLIST_GET(it));
6373         it->ln = c->ln;
6374         if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
6375           {
6376              Evas_Object_Textblock_Format_Item *fi;
6377              fi = _ITEM_FORMAT(it);
6378              fi->y = c->y;
6379           }
6380      }
6381    c->ln->items = (Evas_Object_Textblock_Item *)
6382       eina_inlist_append(EINA_INLIST_GET(c->ln->items),
6383             EINA_INLIST_GET(_ITEM(ellip_ti)));
6384    _ITEM(ellip_ti)->ln = c->ln;
6385 
6386    c->position = (c->position == TEXTBLOCK_POSITION_START) ?
6387       TEXTBLOCK_POSITION_SINGLE : TEXTBLOCK_POSITION_END;
6388    _layout_line_finalize(c, ellip_ti->parent.format);
6389 }
6390 
6391 /* Don't do much for the meanwhile. */
6392 static inline void
_layout_paragraph_render(Efl_Canvas_Textblock_Data * o,Evas_Object_Textblock_Paragraph * par)6393 _layout_paragraph_render(Efl_Canvas_Textblock_Data *o,
6394                          Evas_Object_Textblock_Paragraph *par)
6395 {
6396    if (par->rendered)
6397       return;
6398    par->rendered = EINA_TRUE;
6399 
6400    (void) o;
6401 }
6402 
6403 /* calculates items width in current paragraph */
6404 static inline Evas_Coord
_calc_items_width(Ctxt * c)6405 _calc_items_width(Ctxt *c)
6406 {
6407    Evas_Object_Textblock_Item *it, *last_it = NULL;
6408    Eina_List *i;
6409    Evas_Coord w = 0;
6410 
6411    if  (!c->par->logical_items)
6412       return 0;
6413 
6414    EINA_LIST_FOREACH(c->par->logical_items, i, it)
6415      {
6416         w += it->adv;
6417         last_it = it;
6418      }
6419 
6420    //reaching this point when it is the last item
6421    if (last_it)
6422       w += last_it->w - last_it->adv;
6423    return w;
6424 }
6425 
6426 static inline int
_item_get_cutoff(Ctxt * c,Evas_Object_Textblock_Item * it,Evas_Coord x,Evas_Coord width_offset)6427 _item_get_cutoff(Ctxt *c, Evas_Object_Textblock_Item *it, Evas_Coord x, Evas_Coord width_offset)
6428 {
6429    int pos = -1;
6430    Evas_Object_Textblock_Text_Item *ti;
6431    Evas_Object_Protected_Data *obj = c->evas_o;
6432 
6433    ti = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(it) : NULL;
6434    if (ti && ti->parent.format->font.font)
6435      {
6436         pos = ENFN->font_last_up_to_pos(ENC, ti->parent.format->font.font,
6437               &ti->text_props, x, 0, width_offset);
6438      }
6439    return pos;
6440 }
6441 
6442 /**
6443  * @internal
6444  * This handles ellipsis prior most of the work in _layout_par.
6445  * Currently it is here to handle all value in the range of 0.0 to 0.9999 (<1).
6446  * It starts by getting the total width of items, and calculates the 'block' of
6447  * text that needs to be removed i.e. sets low and high boundaries
6448  * of that block.
6449  * All text items that intersect this block will be cut: the edge items (ones
6450  * that don't intersect in whole) will be split, and the rest are set to be
6451  * visually-deleted.
6452  * Note that a special case for visible format items does not
6453  * split them, but instead just visually-deletes them (because there are no
6454  * characters to split).
6455  */
6456 static inline void
_layout_par_ellipsis_items(Ctxt * c,double ellip)6457 _layout_par_ellipsis_items(Ctxt *c, double ellip)
6458 {
6459    Evas_Object_Textblock_Item *it;
6460    Evas_Object_Textblock_Text_Item *ellip_ti;
6461    Eina_List *i, *j;
6462    Evas_Coord items_width, exceed, items_cut;
6463    Evas_Coord l, h, off;
6464    int pos;
6465 
6466    c->ellip_prev_it = NULL;
6467 
6468    /* calc exceed amount */
6469    items_width = _calc_items_width(c);
6470    exceed = items_width - (c->w - c->o->style_pad.l - c->o->style_pad.r
6471                          - c->marginl - c->marginr);
6472 
6473    if (exceed <= 0)
6474       return;
6475 
6476      {
6477         Evas_Object_Textblock_Item *first_it =
6478            _ITEM(eina_list_data_get(c->par->logical_items));
6479         ellip_ti = _layout_ellipsis_item_new(c, first_it);
6480      }
6481    exceed += ellip_ti->parent.adv;
6482    items_cut = items_width * ellip;
6483    l = items_cut - (exceed * ellip);
6484    h = l + exceed; //h = items_cut - (exceed * (1 - ellip))
6485 
6486    off = 0;
6487    /* look for the item that is being cut by the lower boundary */
6488    i = c->par->logical_items;
6489    EINA_LIST_FOREACH(c->par->logical_items, i, it)
6490      {
6491         if (it->w > (l - off))
6492            break;
6493         off += it->adv;
6494      }
6495    c->ellip_prev_it = i;
6496    if (it) ellip_ti = _layout_ellipsis_item_new(c, it);
6497 
6498 
6499    pos = (it && it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
6500       (_item_get_cutoff(c, it, l - off, ellip_ti->parent.w)) : -1;
6501    if (pos >= 0)
6502      {
6503         _layout_item_text_split_strip_white(c, _ITEM_TEXT(it), i, pos);
6504         off += it->adv;
6505         i = eina_list_next(i);
6506      }
6507 
6508    /* look for the item that is being cut by the upper boundary */
6509    EINA_LIST_FOREACH(i, j, it)
6510      {
6511         if (it->w > (h - off))
6512            break;
6513         off += it->adv;
6514         /* if item is not being cut by the upper boundary, then
6515          * it is contained in the area that we are supposed to
6516          * visually remove */
6517         it->visually_deleted = EINA_TRUE;
6518      }
6519 
6520    pos = (it && it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
6521       (_item_get_cutoff(c, it, h - off, 0)) : -1;
6522    if (pos >= 0)
6523       _layout_item_text_split_strip_white(c, _ITEM_TEXT(it), j, pos + 1);
6524    if (it)
6525       it->visually_deleted = EINA_TRUE;
6526 }
6527 
6528 static inline void
_layout_par_append_ellipsis(Ctxt * c)6529 _layout_par_append_ellipsis(Ctxt *c)
6530 {
6531    Evas_Object_Textblock_Text_Item *ellip_ti = c->o->ellip_ti;
6532    c->ln->items = (Evas_Object_Textblock_Item *)
6533       eina_inlist_append(EINA_INLIST_GET(c->ln->items),
6534             EINA_INLIST_GET(_ITEM(ellip_ti)));
6535    ellip_ti->parent.ln = c->ln;
6536    c->x += ellip_ti->parent.adv;
6537 }
6538 /* obstacles */
6539 static inline void
6540 _layout_obstacles_update(Ctxt *c);
6541 
6542 typedef struct _Evas_Textblock_Obstacle
6543 {
6544    Eo *eo_obs; /**< Pointer to evas object which serves as an obstacle. */
6545    Evas_Coord x, y, w, h; /**< Geometry of the obstacle object. x,y are
6546    the offset position of the obstacle relative to the textblock object. */
6547    Eina_Bool visible : 1;
6548 } Evas_Textblock_Obstacle;
6549 
6550 static Evas_Textblock_Obstacle *
6551 _layout_item_obstacle_get(Ctxt *c, Evas_Object_Textblock_Item *it);
6552 
6553 /* 0 means go ahead, 1 means break without an error, 2 means
6554  * break with an error, should probably clean this a bit (enum/macro)
6555  * FIXME ^ */
6556 static int
_layout_par(Ctxt * c)6557 _layout_par(Ctxt *c)
6558 {
6559    Evas_Object_Textblock_Item *it;
6560    Eina_List *i;
6561    int ret = 0;
6562    int wrap = -1;
6563    char *line_breaks = NULL;
6564    char *word_breaks = NULL;
6565 
6566    if (!c->par->logical_items)
6567      return 2;
6568 
6569    /* We want to show it. */
6570    c->par->visible = 1;
6571 
6572    /* Check if we need to skip this paragraph because it's already layouted
6573     * correctly, and mark handled nodes as dirty. */
6574    c->par->line_no = c->line_no;
6575 
6576    if (c->par->text_node)
6577      {
6578         /* Skip this paragraph if width is the same, there is no ellipsis
6579          * and we aren't just calculating. */
6580         if (!c->par->text_node->is_new && !c->par->text_node->dirty &&
6581               !c->width_changed && c->par->lines &&
6582               !c->o->have_ellipsis && !c->o->obstacle_changed &&
6583               !c->o->wrap_changed)
6584           {
6585              Evas_Object_Textblock_Line *ln;
6586              /* Update c->line_no */
6587              ln = (Evas_Object_Textblock_Line *)
6588                 EINA_INLIST_GET(c->par->lines)->last;
6589              if (ln)
6590                 c->line_no = c->par->line_no + ln->line_no + 1;
6591 
6592              /* After this par we are no longer at the beginning, as there
6593               * must be some text in the par. */
6594              if (!EINA_INLIST_GET(c->par)->next)
6595                {
6596                   c->position = (c->position == TEXTBLOCK_POSITION_START) ?
6597                      TEXTBLOCK_POSITION_SINGLE : TEXTBLOCK_POSITION_END;
6598                }
6599              else
6600                {
6601                   if (c->position == TEXTBLOCK_POSITION_START)
6602                      c->position = TEXTBLOCK_POSITION_ELSE;
6603                }
6604 
6605              if (c->par->last_fw > c->wmax) c->wmax = c->par->last_fw;
6606              return 0;
6607           }
6608 
6609         c->par->text_node->dirty = EINA_FALSE;
6610         c->par->text_node->is_new = EINA_FALSE;
6611         c->par->rendered = EINA_FALSE;
6612 
6613         /* Merge back and clear the paragraph */
6614           {
6615              Eina_List *itr, *itr_next;
6616              Evas_Object_Textblock_Item *ititr, *prev_it = NULL;
6617              _paragraph_clear(c->o, c->evas_o, c->par);
6618              EINA_LIST_FOREACH_SAFE(c->par->logical_items, itr, itr_next, ititr)
6619                {
6620                   if (ititr->merge && prev_it &&
6621                         (prev_it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
6622                         (ititr->type == EVAS_TEXTBLOCK_ITEM_TEXT))
6623                     {
6624                        _layout_item_merge_and_free(c, _ITEM_TEXT(prev_it),
6625                              _ITEM_TEXT(ititr));
6626                        c->par->logical_items =
6627                           eina_list_remove_list(c->par->logical_items, itr);
6628                     }
6629                   else
6630                     {
6631                        ititr->visually_deleted = EINA_FALSE;
6632                        prev_it = ititr;
6633                     }
6634                }
6635           }
6636      }
6637 
6638    c->y = c->par->y;
6639 
6640 
6641 #ifdef BIDI_SUPPORT
6642    if (c->par->is_bidi)
6643      {
6644         _layout_update_bidi_props(c->o, c->par);
6645      }
6646 #endif
6647 
6648    it = _ITEM(eina_list_data_get(c->par->logical_items));
6649    if (c->line_no == 0 || c->o->multiline)
6650      {
6651         _layout_line_new(c, it->format);
6652      }
6653    /* We walk on our own because we want to be able to add items from
6654     * inside the list and then walk them on the next iteration. */
6655 
6656    /* TODO: We need to consider where ellipsis is used in the current text.
6657       Currently, we assume that ellipsis is at the beginning of the
6658       paragraph. This is a safe assumption for now, as other usages
6659       seem a bit unnatural.*/
6660    // FIXME: revisit this for multi-line.
6661      {
6662         double ellip;
6663         ellip = it->format->ellipsis;
6664         if ((0 <= ellip) && (ellip < 1.0) && c->line_no == 0)
6665            _layout_par_ellipsis_items(c, ellip);
6666      }
6667 
6668    Eina_Bool item_preadv = EINA_FALSE;
6669    Evas_Textblock_Obstacle *obs = NULL;
6670    c->par->last_fw = 0;
6671    it = NULL;
6672    for (i = c->par->logical_items ; i ; )
6673      {
6674         Evas_Coord prevdescent = 0, prevascent = 0;
6675         int adv_line = 0;
6676         int redo_item = 0;
6677         Evas_Textblock_Obstacle_Info *obs_info = NULL;
6678         Evas_Coord itw;
6679         Evas_Object_Textblock_Item *prev_it = it;
6680 
6681         it = _ITEM(eina_list_data_get(i));
6682         /* Skip visually deleted items */
6683         if (it->visually_deleted ||
6684             ((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) && !it->format->font.font))
6685           {
6686              //one more chance for ellipsis special cases
6687              if (c->ellip_prev_it == i)
6688                 _layout_par_append_ellipsis(c);
6689 
6690              i = eina_list_next(i);
6691              continue;
6692           }
6693 
6694         it->x = c->x;
6695         if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
6696           {
6697              _layout_item_ascent_descent_adjust(c->evas_o, &c->ascent,
6698                    &c->descent, it, it->format);
6699           }
6700         else
6701           {
6702              Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
6703              if (fi->formatme)
6704                {
6705                   prevdescent = c->descent;
6706                   prevascent = c->ascent;
6707                   /* If there are no text items yet, calc ascent/descent
6708                    * according to the current format. */
6709                   if (c->ascent + c->descent == 0)
6710                      _layout_item_ascent_descent_adjust(c->evas_o, &c->ascent,
6711                            &c->descent, it, it->format);
6712 
6713                   _layout_calculate_format_item_size(c->evas_o, fi, &c->ascent,
6714                         &c->descent, &fi->y, &fi->parent.w, &fi->parent.h);
6715                   fi->parent.adv = fi->parent.w;
6716                }
6717           }
6718 
6719         if (c->handle_obstacles && !obs)
6720           {
6721              obs = _layout_item_obstacle_get(c, it);
6722           }
6723         /* Check if we need to wrap, i.e the text is bigger than the width,
6724            or we already found a wrap point. */
6725         itw = it->w;
6726 
6727         if ((c->w >= 0) &&
6728               (obs ||
6729                  (((c->x + itw) >
6730                    (c->w - c->o->style_pad.l - c->o->style_pad.r -
6731                     c->marginl - c->marginr)) || (wrap > 0))))
6732           {
6733              /* Handle ellipsis here. If we don't have more width left
6734               * and no height left, or no more width left and no wrapping.
6735               * Note that this is only for ellipsis == 1.0, and is treated in a
6736               * fast path.
6737               * Other values of 0.0 <= ellipsis < 1.0 are handled in
6738               * _layout_par_ellipsis_items */
6739              int ellip_h_thresh = 0;
6740 
6741              /* Calculate ellipsis threshold height. */
6742                {
6743                   int ascent = 0, descent = 0, maxasc = 0, maxdesc = 0;
6744 
6745                   _layout_item_ascent_descent_adjust(c->evas_o, &ascent, &descent,
6746                         it, it->format);
6747 
6748                   if (c->position == TEXTBLOCK_POSITION_START)
6749                      _layout_item_max_ascent_descent_calc(c->evas_o, &maxasc, &maxdesc,
6750                            it, TEXTBLOCK_POSITION_SINGLE);
6751                   else
6752                      _layout_item_max_ascent_descent_calc(c->evas_o, &maxasc, &maxdesc,
6753                            it, TEXTBLOCK_POSITION_END);
6754 
6755                   if (ascent > maxasc) maxasc = ascent;
6756                   if (descent > maxdesc) maxdesc = descent;
6757 
6758                   /* The ascent/descent of this item + the ascent descent of
6759                    * the next item as if it was the last. */
6760                   ellip_h_thresh = ascent + descent + maxasc + maxdesc;
6761                }
6762 
6763              if ((EINA_DBL_EQ(it->format->ellipsis, 1.0)) && (c->h >= 0) &&
6764                  ((c->y + ellip_h_thresh >
6765                    c->h - c->o->style_pad.t - c->o->style_pad.b) ||
6766                      (!it->format->wrap_word && !it->format->wrap_char &&
6767                          !it->format->wrap_mixed && !it->format->wrap_hyphenation) ||
6768                      !c->o->multiline))
6769                {
6770                   _layout_handle_ellipsis(c, it, i);
6771                   ret = 1;
6772                   goto end;
6773                }
6774              /* If we want to wrap and it's worth checking for wrapping
6775               * (i.e there's actually text). */
6776              else if (c->o->multiline &&
6777                    ((wrap > 0) || it->format->wrap_word || it->format->wrap_char ||
6778                 it->format->wrap_mixed || it->format->wrap_hyphenation) && it->text_node)
6779                {
6780                   size_t line_start;
6781                   size_t it_len;
6782 
6783                   it_len = (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) ?
6784                      1 : _ITEM_TEXT(it)->text_props.text_len;
6785 
6786 
6787                   /* If we haven't calculated the linebreaks yet,
6788                    * do */
6789                   if (!line_breaks)
6790                     {
6791                        /* Only relevant in those cases */
6792                        if (it->format->wrap_word || it->format->wrap_mixed ||
6793                            it->format->wrap_hyphenation)
6794                          {
6795                             const char *lang;
6796                             lang = (it->format->font.fdesc) ?
6797                                it->format->font.fdesc->lang : "";
6798                             size_t len =
6799                                eina_ustrbuf_length_get(
6800                                      it->text_node->unicode);
6801                             line_breaks = malloc(len);
6802                             set_linebreaks_utf32((const utf32_t *)
6803                                   eina_ustrbuf_string_get(
6804                                      it->text_node->unicode),
6805                                   len, lang, line_breaks);
6806                          }
6807                     }
6808 
6809                   if (!word_breaks && it->format->wrap_hyphenation)
6810                     {
6811                        const char *lang;
6812                        lang = (it->format->font.fdesc) ?
6813                           it->format->font.fdesc->lang : "";
6814                        size_t len =
6815                           eina_ustrbuf_length_get(
6816                                 it->text_node->unicode);
6817                        word_breaks = malloc(len);
6818                        set_wordbreaks_utf32((const utf32_t *)
6819                              eina_ustrbuf_string_get(
6820                                 it->text_node->unicode),
6821                              len, lang, word_breaks);
6822                     }
6823 
6824                   if (c->ln->items)
6825                      line_start = c->ln->items->text_pos;
6826                   else
6827                      line_start = it->text_pos;
6828 
6829                   /* Only when doing non-obstacle handling */
6830                   if (!obs)
6831                      adv_line = 1;
6832                   /* If we don't already have a wrap point from before */
6833                   if (wrap < 0)
6834                     {
6835                        /* Originally with wrapping, we may have ended up
6836                         * wrapping on the item next to the current one,
6837                         * if the current one was the first item in a line.
6838                         * This is different with obstacles: we allow the
6839                         * wrapping point algorithm to consider the first
6840                         * item in a line as well, and "push" it forward
6841                         * after the obstacle.
6842                         * There is one specific case with obstacles, where
6843                         * we DON'T allow to scan forward on the textblock's
6844                         * edges, and that's if the first item in a line
6845                         * was pushed forward by an obstacle once, as there
6846                         * is a chance it will fit in the next lines. */
6847                        Eina_Bool allow_scan_fwd = (!obs && !item_preadv);
6848                        Evas_Coord cw = c->w;
6849                        if (obs)
6850                          {
6851                             cw = obs->x;
6852                          }
6853                        if (it->format->wrap_word)
6854                           wrap = _layout_get_wordwrap(c, it->format, it,
6855                                 line_start, line_breaks,
6856                                 cw, c->x, allow_scan_fwd);
6857                        else if (it->format->wrap_char)
6858                           wrap = _layout_get_charwrap(c, it->format, it,
6859                                 line_start, line_breaks, cw, c->x);
6860                        else if (it->format->wrap_mixed)
6861                           wrap = _layout_get_mixedwrap(c, it->format, it,
6862                                 line_start, line_breaks, cw, c->x, allow_scan_fwd);
6863                        else if (it->format->wrap_hyphenation)
6864                           wrap = _layout_get_hyphenationwrap(c, it->format, it,
6865                                 line_start, line_breaks, word_breaks,
6866                                 cw, c->x);
6867                        else
6868                           wrap = -1;
6869                     }
6870 
6871                   /* If it's before the item, rollback and apply.
6872                      if it's in the item, cut.
6873                      If it's after the item, delay the cut */
6874                   if (wrap > 0)
6875                     {
6876                        size_t uwrap = (size_t) wrap;
6877                        if (uwrap < it->text_pos)
6878                          {
6879                             /* Rollback latest additions, and cut that
6880                                item */
6881                             i = eina_list_prev(i);
6882                             it = eina_list_data_get(i);
6883                             while (uwrap < it->text_pos)
6884                               {
6885                                  c->ln->items = _ITEM(
6886                                        eina_inlist_remove(
6887                                           EINA_INLIST_GET(c->ln->items),
6888                                           EINA_INLIST_GET(it)));
6889                                  it->ln = c->ln;
6890                                  i = eina_list_prev(i);
6891                                  it = eina_list_data_get(i);
6892                               }
6893                             c->x = it->x;
6894                             c->ln->items = _ITEM(
6895                                   eina_inlist_remove(
6896                                      EINA_INLIST_GET(c->ln->items),
6897                                      EINA_INLIST_GET(it)));
6898                             continue;
6899                          }
6900                        /* If it points to the end, it means the previous
6901                         * char is a whitespace we should remove, so this
6902                         * is a wanted cutting point. */
6903                        else if (uwrap > it->text_pos + it_len)
6904                          {
6905                             /* FIXME: Should redo the ellipsis handling.
6906                              * If we can do ellipsis, just cut here. */
6907                             if (EINA_DBL_EQ(it->format->ellipsis, 1.0))
6908                               {
6909                                  _layout_handle_ellipsis(c, it, i);
6910                                  ret = 1;
6911                                  goto end;
6912                               }
6913                             else
6914                               {
6915                                  /* Delay the cut in a smart way i.e use the
6916                                     item_pos as the line_start, because
6917                                     there's already no cut before*/
6918                                  wrap = -1;
6919                                  adv_line = 0;
6920                               }
6921                          }
6922                        else
6923                          {
6924                             wrap -= it->text_pos; /* Cut here */
6925                          }
6926                     }
6927                   /* Specific case for obstacles */
6928                   if (obs && (wrap >= 0))
6929                     {
6930                        obs_info = calloc(1, sizeof(Evas_Textblock_Obstacle_Info));
6931                        obs_info->it = it;
6932                        c->obs_infos = eina_list_append(c->obs_infos, obs_info);
6933                     }
6934                   if ((wrap >= 0) && ((size_t) wrap == it_len))
6935                     {
6936                        /* Can happen if this is the last word in the paragraph */
6937                        adv_line = 0;
6938                     }
6939                   else if (wrap > 0)
6940                     {
6941                        if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
6942                          {
6943                             _layout_item_text_split_strip_white(c,
6944                                   _ITEM_TEXT(it), i, wrap);
6945                          }
6946                        if (obs)
6947                          {
6948                             obs_info->obs_adv = obs->x + obs->w - c->x - it->adv;
6949                             c->x = obs->x + obs->w;
6950                          }
6951                     }
6952                   else if (wrap == 0)
6953                     {
6954                        /* Should wrap before the item */
6955                        if (prev_it && (prev_it->type == EVAS_TEXTBLOCK_ITEM_TEXT))
6956                          {
6957                             _layout_item_text_split_strip_white(c,
6958                                   _ITEM_TEXT(prev_it), eina_list_prev(i),
6959                                   _ITEM_TEXT(prev_it)->text_props.text_len);
6960                          }
6961 
6962                        /* We didn't end up using the item, so revert the ascent
6963                         * and descent changes. */
6964                        c->descent = prevdescent;
6965                        c->ascent = prevascent;
6966 
6967                        adv_line = 0;
6968                        redo_item = 1;
6969                        if (obs)
6970                          {
6971                             obs_info->obs_preadv = obs->x + obs->w - c->x;
6972                             c->x = obs->x + obs->w;
6973                             item_preadv = EINA_TRUE;
6974                          }
6975                        else
6976                          {
6977                             _layout_line_advance(c, it->format);
6978 
6979                             if (c->vertical_ellipsis)
6980                               {
6981                                  ret = 1;
6982                                  goto end;
6983                               }
6984 
6985                             item_preadv = EINA_FALSE;
6986                          }
6987                     }
6988                   else // (wrap < 0)
6989                     {
6990                        /* avoid line advance if there is no wrapping point */
6991                        adv_line = 0;
6992                     }
6993                   /* Reset wrap */
6994                   obs = NULL;
6995                   wrap = -1;
6996                }
6997           }
6998 
6999         if (!redo_item && !it->visually_deleted)
7000           {
7001              c->ln->items = (Evas_Object_Textblock_Item *)
7002                 eina_inlist_append(EINA_INLIST_GET(c->ln->items),
7003                       EINA_INLIST_GET(it));
7004              it->ln = c->ln;
7005              if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
7006                {
7007                   Evas_Object_Textblock_Format_Item *fi;
7008                   fi = _ITEM_FORMAT(it);
7009                   fi->y = c->y;
7010                   /* If it's a newline, and we are not in newline compat
7011                    * mode, or we are in newline compat mode, and this is
7012                    * not used as a paragraph separator, advance */
7013                   if (c->o->multiline &&
7014                         fi->item && _IS_LINE_SEPARATOR(fi->item) &&
7015                         (!c->o->legacy_newline ||
7016                          eina_list_next(i)))
7017                     {
7018                        adv_line = 1;
7019                     }
7020                }
7021              if (!obs_info) c->x += it->adv;
7022              if (c->ellip_prev_it == i)
7023                 _layout_par_append_ellipsis(c);
7024              i = eina_list_next(i);
7025              item_preadv = EINA_FALSE;
7026           }
7027         if (adv_line)
7028           {
7029              /* Each line is according to the first item in it, and here
7030               * i is already the next item (or the current if we redo it) */
7031              if (i)
7032                {
7033                   it = _ITEM(eina_list_data_get(i));
7034                }
7035              _layout_line_advance(c, it->format);
7036 
7037              if (c->vertical_ellipsis)
7038                {
7039                   ret = 1;
7040                   goto end;
7041                }
7042           }
7043      }
7044 
7045    if (c->ln->items)
7046      {
7047         if (!EINA_INLIST_GET(c->par)->next)
7048           {
7049              c->position = (c->position == TEXTBLOCK_POSITION_START) ?
7050                 TEXTBLOCK_POSITION_SINGLE : TEXTBLOCK_POSITION_END;
7051           }
7052 
7053         /* Here 'it' is the last format used */
7054         _layout_line_finalize(c, it->format);
7055 
7056         if (c->vertical_ellipsis)
7057           ret = 1;
7058      }
7059 
7060 end:
7061    if (line_breaks)
7062       free(line_breaks);
7063    if (word_breaks)
7064       free(word_breaks);
7065 
7066 #ifdef BIDI_SUPPORT
7067    if (c->par->bidi_props)
7068      {
7069         evas_bidi_paragraph_props_unref(c->par->bidi_props);
7070         c->par->bidi_props = NULL;
7071      }
7072 #endif
7073 
7074    return ret;
7075 }
7076 
7077 /**
7078  * @internal
7079  * Invalidate text nodes according to format changes
7080  * This goes through all the new format changes and marks the text nodes
7081  * that should be invalidated because of format changes.
7082  *
7083  * @param c the working context.
7084  */
7085 static inline void
_format_changes_invalidate_text_nodes(Ctxt * c)7086 _format_changes_invalidate_text_nodes(Ctxt *c)
7087 {
7088    Evas_Object_Textblock_Node_Format *fnode = c->o->format_nodes;
7089    Evas_Object_Textblock_Node_Text *start_n = NULL;
7090    Eina_List *fstack = NULL;
7091    int balance = 0;
7092    while (fnode)
7093      {
7094         if (fnode->is_new)
7095           {
7096              const char *fstr = fnode->orig_format;
7097              /* balance < 0 means we gave up and everything should be
7098               * invalidated */
7099              if (fnode->opener && !fnode->own_closer)
7100                {
7101                   balance++;
7102                   if (!fstack)
7103                      start_n = fnode->text_node;
7104                   fstack = eina_list_prepend(fstack, fnode);
7105                }
7106              else if (!fnode->opener)
7107                {
7108                   size_t fstr_len;
7109                   fstr_len = strlen(fstr);
7110                   /* Generic popper, just pop */
7111                   if (((fstr[0] == '/') && !fstr[1]) || !fstr[0])
7112                     {
7113                        fstack = eina_list_remove_list(fstack, fstack);
7114                        balance--;
7115                     }
7116                   /* Find the matching format and pop it, if the matching format
7117                    * is out format, i.e the last one, pop and break. */
7118                   else
7119                     {
7120                        Eina_List *i;
7121                        Evas_Object_Textblock_Node_Format *fnode2;
7122                        EINA_LIST_FOREACH(fstack, i, fnode2)
7123                          {
7124                             if (_FORMAT_IS_CLOSER_OF(
7125                                      fnode2->orig_format, fstr + 1, fstr_len - 1))
7126                               {
7127                                  fstack = eina_list_remove_list(fstack, i);
7128                                  break;
7129                               }
7130                          }
7131                        balance--;
7132                     }
7133 
7134                   if (!fstack)
7135                     {
7136                        Evas_Object_Textblock_Node_Text *f_tnode =
7137                           fnode->text_node;
7138                        while (start_n)
7139                          {
7140                             start_n->dirty = EINA_TRUE;
7141                             if (start_n == f_tnode)
7142                                break;
7143                             start_n =
7144                                _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
7145                          }
7146                        start_n = NULL;
7147                     }
7148                }
7149              else if (!fnode->visible)
7150                 balance = -1;
7151 
7152              if (balance < 0)
7153                {
7154                   /* if we don't already have a starting point, use the
7155                    * current paragraph. */
7156                   if (!start_n)
7157                      start_n = fnode->text_node;
7158                   break;
7159                }
7160           }
7161         fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7162      }
7163 
7164    if (balance != 0)
7165      {
7166         while (start_n)
7167           {
7168              start_n->dirty = EINA_TRUE;
7169              start_n = _NODE_TEXT(EINA_INLIST_GET(start_n)->next);
7170           }
7171      }
7172 
7173    eina_list_free(fstack);
7174 }
7175 
7176 static Layout_Text_Append_Queue *
_layout_text_append_queue_item_append(Layout_Text_Append_Queue * queue,Evas_Object_Textblock_Format * format,size_t start,int off)7177 _layout_text_append_queue_item_append(Layout_Text_Append_Queue *queue,
7178       Evas_Object_Textblock_Format *format, size_t start, int off)
7179 {
7180    /* Don't add empty items. */
7181    if (off == 0)
7182       return (Layout_Text_Append_Queue *) queue;
7183 
7184    Layout_Text_Append_Queue *item = calloc(1, sizeof(*item));
7185    item->format = format;
7186    item->start = start;
7187    item->off = off;
7188    item->format->ref++;
7189 
7190    return (Layout_Text_Append_Queue *) eina_inlist_append(EINA_INLIST_GET(queue), EINA_INLIST_GET(item));
7191 }
7192 
7193 static void
_layout_text_append_item_free(Ctxt * c,Layout_Text_Append_Queue * item)7194 _layout_text_append_item_free(Ctxt *c, Layout_Text_Append_Queue *item)
7195 {
7196    if (item->format)
7197       _format_unref_free(c->evas_o, item->format);
7198    free(item);
7199 }
7200 
7201 static void
_layout_text_append_commit(Ctxt * c,Layout_Text_Append_Queue ** _queue,Evas_Object_Textblock_Node_Text * n,Eina_List * rel)7202 _layout_text_append_commit(Ctxt *c, Layout_Text_Append_Queue **_queue, Evas_Object_Textblock_Node_Text *n, Eina_List *rel)
7203 {
7204    Layout_Text_Append_Queue *item, *queue = *_queue;
7205 
7206    if (!queue)
7207       return;
7208 
7209      {
7210         item = (Layout_Text_Append_Queue *) EINA_INLIST_GET(queue)->last;
7211         int off = item->start - queue->start + item->off;
7212         _layout_text_append(c, queue, n, queue->start, off, c->o->repch, rel);
7213      }
7214 
7215    while (queue)
7216      {
7217         item = queue;
7218         queue = (Layout_Text_Append_Queue *) EINA_INLIST_GET(queue)->next;
7219         _layout_text_append_item_free(c, item);
7220      }
7221 
7222    *_queue = NULL;
7223 }
7224 
7225 static Eina_Bool
_layout_split_text_because_format(const Evas_Object_Textblock_Format * fmt,const Evas_Object_Textblock_Format * nfmt)7226 _layout_split_text_because_format(const Evas_Object_Textblock_Format *fmt,
7227       const Evas_Object_Textblock_Format *nfmt)
7228 {
7229    if ((fmt->password != nfmt->password) ||
7230          memcmp(&fmt->font, &nfmt->font, sizeof(fmt->font)))
7231      {
7232         return EINA_TRUE;
7233      }
7234 
7235    return EINA_FALSE;
7236 }
7237 
7238 /** FIXME: Document */
7239 static void
_layout_pre(Ctxt * c)7240 _layout_pre(Ctxt *c)
7241 {
7242    int *style_pad_l, *style_pad_r, *style_pad_t, *style_pad_b;
7243    Evas_Object *eo_obj = c->obj;
7244    Efl_Canvas_Textblock_Data *o = c->o;
7245 
7246    style_pad_l = &c->style_pad.l;
7247    style_pad_r = &c->style_pad.r;
7248    style_pad_b = &c->style_pad.b;
7249    style_pad_t = &c->style_pad.t;
7250 
7251    /* Mark text nodes as dirty if format have changed. */
7252    if (o->format_changed)
7253      _format_changes_invalidate_text_nodes(c);
7254 
7255    if (o->content_changed)
7256      {
7257         Evas_Object_Textblock_Node_Text *n;
7258         c->o->have_ellipsis = 0;
7259         c->par = c->paragraphs = o->paragraphs;
7260         /* Go through all the text nodes to create the logical layout */
7261         EINA_INLIST_FOREACH(c->o->text_nodes, n)
7262           {
7263              Evas_Object_Textblock_Node_Format *fnode;
7264              size_t start;
7265              int off;
7266 
7267              /* If it's not a new paragraph, either update it or skip it.
7268               * Remove all the paragraphs that were deleted */
7269              if (!n->is_new)
7270                {
7271                   /* Remove all the deleted paragraphs at this point */
7272                   while (c->par->text_node != n)
7273                     {
7274                        Evas_Object_Textblock_Paragraph *tmp_par =
7275                           (Evas_Object_Textblock_Paragraph *)
7276                           EINA_INLIST_GET(c->par)->next;
7277 
7278                        c->paragraphs = (Evas_Object_Textblock_Paragraph *)
7279                           eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
7280                                 EINA_INLIST_GET(c->par));
7281                        _paragraph_free(c->o, c->evas_o, c->par);
7282 
7283                        c->par = tmp_par;
7284                     }
7285 
7286                   /* If it's dirty, remove and recreate, if it's clean,
7287                    * skip to the next. */
7288                   if (n->dirty)
7289                     {
7290                        Evas_Object_Textblock_Paragraph *prev_par = c->par;
7291 
7292                        _layout_paragraph_new(c, n, EINA_TRUE);
7293 
7294                        c->paragraphs = (Evas_Object_Textblock_Paragraph *)
7295                           eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
7296                                 EINA_INLIST_GET(prev_par));
7297                        _paragraph_free(c->o, c->evas_o, prev_par);
7298                     }
7299                   else
7300                     {
7301                        c->par = (Evas_Object_Textblock_Paragraph *)
7302                           EINA_INLIST_GET(c->par)->next;
7303 
7304                        /* Update the format stack according to the node's
7305                         * formats */
7306                        fnode = n->format_node;
7307                        while (fnode && (fnode->text_node == n))
7308                          {
7309                             /* Only do this if this actually changes format */
7310                             if (fnode->format_change)
7311                               {
7312                                  int pl = 0, pr = 0, pt = 0, pb = 0;
7313                                  _layout_do_format(eo_obj, c, &c->fmt, fnode,
7314                                                    &pl, &pr, &pt, &pb, EINA_FALSE);
7315                                  fnode->pad.l = pl;
7316                                  fnode->pad.r = pr;
7317                                  fnode->pad.t = pt;
7318                                  fnode->pad.b = pb;
7319                               }
7320                             if (fnode->pad.l > *style_pad_l) *style_pad_l = fnode->pad.l;
7321                             if (fnode->pad.r > *style_pad_r) *style_pad_r = fnode->pad.r;
7322                             if (fnode->pad.t > *style_pad_t) *style_pad_t = fnode->pad.t;
7323                             if (fnode->pad.b > *style_pad_b) *style_pad_b = fnode->pad.b;
7324                             fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7325                          }
7326                        continue;
7327                     }
7328                }
7329              else
7330                {
7331                   /* If it's a new paragraph, just add it. */
7332                   if (!_layout_paragraph_new(c, n, EINA_FALSE))
7333                     break;
7334                }
7335 
7336 #ifdef BIDI_SUPPORT
7337              _layout_update_bidi_props(c->o, c->par);
7338 #endif
7339 
7340              /* For each text node, go through all of it's format nodes, and
7341               * append text from the start to the offset of the next format
7342               * using the last format got. If needed it also creates format
7343               * items this is the core algorithm of the layout mechanism.
7344               * Skip the unicode replacement chars when there are because
7345               * we don't want to print them. */
7346              Layout_Text_Append_Queue *queue = NULL;
7347              fnode = n->format_node;
7348              start = off = 0;
7349              while (fnode && (fnode->text_node == n))
7350                {
7351                   Evas_Object_Textblock_Format_Item *fi = NULL;
7352                   Evas_Object_Textblock_Format *pfmt = c->fmt;
7353                   pfmt->ref++;
7354 
7355                   off += fnode->offset;
7356                   /* No need to skip on the first run, or a non-visible one */
7357                   queue = _layout_text_append_queue_item_append(queue, c->fmt, start, off);
7358                   fi = _layout_do_format(eo_obj, c, &c->fmt, fnode, style_pad_l,
7359                         style_pad_r, style_pad_t, style_pad_b, EINA_TRUE);
7360 
7361                   if (fi || _layout_split_text_because_format(pfmt, c->fmt))
7362                     {
7363                        Eina_List *rel = NULL;
7364                        if (fi)
7365                          {
7366                             rel = eina_list_last(c->par->logical_items);
7367                          }
7368 
7369                        _layout_text_append_commit(c, &queue, n, rel);
7370                     }
7371 
7372                   _format_unref_free(c->evas_o, pfmt);
7373 
7374                   if ((c->have_underline2) || (c->have_underline))
7375                     {
7376                        if (*style_pad_b < c->underline_extend)
7377                          *style_pad_b = c->underline_extend;
7378                        c->have_underline = 0;
7379                        c->have_underline2 = 0;
7380                        c->underline_extend = 0;
7381                     }
7382                   start += off;
7383                   if (fnode->visible)
7384                     {
7385                        off = -1;
7386                        start++;
7387                     }
7388                   else
7389                     {
7390                        off = 0;
7391                     }
7392                   fnode->is_new = EINA_FALSE;
7393                   fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
7394                }
7395              queue = _layout_text_append_queue_item_append(queue, c->fmt, start,
7396                    eina_ustrbuf_length_get(n->unicode) - start);
7397              _layout_text_append_commit(c, &queue, n, NULL);
7398 #ifdef BIDI_SUPPORT
7399              /* Clear the bidi props because we don't need them anymore. */
7400              if (c->par->bidi_props)
7401                {
7402                   evas_bidi_paragraph_props_unref(c->par->bidi_props);
7403                   c->par->bidi_props = NULL;
7404                }
7405 #endif
7406              c->par = (Evas_Object_Textblock_Paragraph *)
7407                 EINA_INLIST_GET(c->par)->next;
7408           }
7409 
7410         /* Delete the rest of the layout paragraphs */
7411         while (c->par)
7412           {
7413              Evas_Object_Textblock_Paragraph *tmp_par =
7414                 (Evas_Object_Textblock_Paragraph *)
7415                 EINA_INLIST_GET(c->par)->next;
7416 
7417              c->paragraphs = (Evas_Object_Textblock_Paragraph *)
7418                 eina_inlist_remove(EINA_INLIST_GET(c->paragraphs),
7419                       EINA_INLIST_GET(c->par));
7420              _paragraph_free(c->o, c->evas_o, c->par);
7421 
7422              c->par = tmp_par;
7423           }
7424         o->paragraphs = c->paragraphs;
7425         c->par = NULL;
7426      }
7427    else
7428      {
7429         if (o->style_pad.l > *style_pad_l) *style_pad_l = o->style_pad.l;
7430         if (o->style_pad.r > *style_pad_r) *style_pad_r = o->style_pad.r;
7431         if (o->style_pad.t > *style_pad_t) *style_pad_t = o->style_pad.t;
7432         if (o->style_pad.b > *style_pad_b) *style_pad_b = o->style_pad.b;
7433      }
7434    /* If there are no paragraphs, create the minimum needed,
7435     * if the last paragraph has no lines/text, create that as well */
7436    if (!c->paragraphs)
7437      {
7438         if (!_layout_paragraph_new(c, NULL, EINA_TRUE)) return;
7439         o->paragraphs = c->paragraphs;
7440      }
7441    c->par = (Evas_Object_Textblock_Paragraph *)
7442       EINA_INLIST_GET(c->paragraphs)->last;
7443    if (!c->par->logical_items)
7444      {
7445         Evas_Object_Textblock_Text_Item *ti;
7446         ti = _layout_text_item_new(c, c->fmt);
7447         ti->parent.text_node = c->par->text_node;
7448         ti->parent.text_pos = 0;
7449         _layout_text_add_logical_item(c, ti, NULL);
7450      }
7451    /* End of logical layout creation */
7452 }
7453 
7454 static void
_layout_visual(Ctxt * c)7455 _layout_visual(Ctxt *c)
7456 {
7457    /* Start of visual layout creation */
7458    {
7459       Evas_Object_Textblock_Paragraph *last_vis_par = NULL;
7460       int par_index_step = c->o->num_paragraphs / TEXTBLOCK_PAR_INDEX_SIZE;
7461       int par_count = 1; /* Force it to take the first one */
7462       int par_index_pos = 0;
7463 
7464       c->position = TEXTBLOCK_POSITION_START;
7465 
7466       if (par_index_step == 0) par_index_step = 1;
7467 
7468       /* Clear all of the index */
7469       memset(c->o->par_index, 0, sizeof(c->o->par_index));
7470 
7471       EINA_INLIST_FOREACH(c->paragraphs, c->par)
7472         {
7473            _layout_update_par(c);
7474 
7475            /* Break if we should stop here. */
7476            if (_layout_par(c))
7477              {
7478                 last_vis_par = c->par;
7479                 break;
7480              }
7481 
7482            if ((par_index_pos < TEXTBLOCK_PAR_INDEX_SIZE) && (--par_count == 0))
7483              {
7484                 par_count = par_index_step;
7485 
7486                 c->o->par_index[par_index_pos++] = c->par;
7487              }
7488         }
7489 
7490       /* Clear the rest of the paragraphs and mark as invisible */
7491       if (c->par)
7492         {
7493            if (c->vertical_ellipsis)
7494              {
7495                 c->vertical_ellipsis = EINA_FALSE;
7496 
7497                 /* If there is no lines, go to the previous paragraph */
7498                 if (!c->par->lines)
7499                   c->par = (Evas_Object_Textblock_Paragraph *)EINA_INLIST_GET(c->par)->prev;
7500 
7501                 if (c->par)
7502                   {
7503                      if (c->par->lines)
7504                        c->ln = (Evas_Object_Textblock_Line *)EINA_INLIST_GET(c->par->lines)->last;
7505 
7506                      if (c->ln && c->ln->items)
7507                        {
7508                           /* Ellipsize previous line */
7509                           Evas_Object_Textblock_Item *last_it, *it;
7510                           Eina_List *i;
7511 
7512                           last_it = _ITEM(EINA_INLIST_GET(c->ln->items)->last);
7513                           c->ln->items = (Evas_Object_Textblock_Item *)eina_inlist_remove(
7514                              EINA_INLIST_GET(c->ln->items), EINA_INLIST_GET(last_it));
7515                           EINA_LIST_FOREACH(c->par->logical_items, i, it)
7516                             {
7517                                if (last_it == it)
7518                                  break;
7519                             }
7520 
7521                           /* Reset previous data before ellipsis */
7522                           c->y -= c->ln->h;
7523                           c->ln->x = c->ln->y = c->ln->w = c->ln->h = 0;
7524                           c->ascent = c->descent = 0;
7525                           c->maxascent = c->maxdescent = 0;
7526                           c->x = last_it->x;
7527 #ifdef BIDI_SUPPORT
7528                           if (c->par->is_bidi)
7529                                _layout_update_bidi_props(c->o, c->par);
7530 #endif
7531 
7532                           _layout_handle_ellipsis(c, last_it, i);
7533 
7534 #ifdef BIDI_SUPPORT
7535                           if (c->par->bidi_props)
7536                             {
7537                                evas_bidi_paragraph_props_unref(c->par->bidi_props);
7538                                c->par->bidi_props = NULL;
7539                             }
7540 #endif
7541                        }
7542                      last_vis_par = c->par;
7543                   }
7544              }
7545 
7546            if (c->par)
7547              {
7548                 c->par = (Evas_Object_Textblock_Paragraph *)
7549                    EINA_INLIST_GET(c->par)->next;
7550                 while (c->par)
7551                   {
7552                      c->par->visible = 0;
7553                      _paragraph_clear(c->o, c->evas_o, c->par);
7554                      c->par = (Evas_Object_Textblock_Paragraph *)
7555                         EINA_INLIST_GET(c->par)->next;
7556                   }
7557              }
7558         }
7559 
7560       /* Get the last visible paragraph in the layout */
7561       if (!last_vis_par)
7562          last_vis_par = (Evas_Object_Textblock_Paragraph *)
7563             EINA_INLIST_GET(c->paragraphs)->last;
7564 
7565       if (last_vis_par)
7566         {
7567            c->hmax = last_vis_par->y + last_vis_par->h +
7568               _layout_last_line_max_descent_adjust_calc(c, last_vis_par) -
7569               c->style_pad.t - c->style_pad.b;
7570         }
7571    }
7572 
7573 }
7574 
7575 static void _layout(const Evas_Object *eo_obj, int w, int h, int *w_ret, int *h_ret);
7576 
7577 static void
_layout_done(Ctxt * c,Evas_Coord * w_ret,Evas_Coord * h_ret)7578 _layout_done(Ctxt *c, Evas_Coord *w_ret, Evas_Coord *h_ret)
7579 {
7580    /* Clean the rest of the format stack */
7581    while (c->format_stack)
7582      {
7583         c->fmt = c->format_stack->data;
7584         c->format_stack = eina_list_remove_list(c->format_stack, c->format_stack);
7585         _format_unref_free(c->evas_o, c->fmt);
7586      }
7587 
7588    if (w_ret) *w_ret = c->wmax;
7589    if (h_ret) *h_ret = c->hmax;
7590 
7591    /* Vertically align the textblock */
7592    if ((c->o->valign > 0.0) && (c->h > c->hmax))
7593      {
7594         Evas_Coord adjustment = (c->h - c->hmax - c->style_pad.t - c->style_pad.b) * c->o->valign;
7595         Evas_Object_Textblock_Paragraph *par;
7596         EINA_INLIST_FOREACH(c->paragraphs, par)
7597           {
7598              par->y += adjustment;
7599           }
7600      }
7601 
7602    if ((c->o->style_pad.l != c->style_pad.l) || (c->o->style_pad.r != c->style_pad.r) ||
7603        (c->o->style_pad.t != c->style_pad.t) || (c->o->style_pad.b != c->style_pad.b))
7604      {
7605         c->o->style_pad.l = c->style_pad.l;
7606         c->o->style_pad.r = c->style_pad.r;
7607         c->o->style_pad.t = c->style_pad.t;
7608         c->o->style_pad.b = c->style_pad.b;
7609         _paragraphs_clear(c);
7610         LYDBG("ZZ: ... layout #2\n");
7611         c->o->content_changed = 0;
7612         _layout(c->obj, c->w, c->h, w_ret, h_ret);
7613         efl_event_callback_call(c->obj, EFL_CANVAS_TEXTBLOCK_EVENT_STYLE_INSETS_CHANGED, NULL);
7614 
7615         c->o->obstacle_changed = EINA_FALSE;
7616      }
7617    else
7618      {
7619         efl_event_callback_call(c->obj, EFL_CANVAS_TEXTBLOCK_EVENT_LAYOUT_FINISHED, NULL);
7620      }
7621 }
7622 
7623 static Eina_Bool
_layout_setup(Ctxt * c,const Eo * eo_obj,Evas_Coord w,Evas_Coord h)7624 _layout_setup(Ctxt *c, const Eo *eo_obj, Evas_Coord w, Evas_Coord h)
7625 {
7626    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
7627    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
7628    Evas *eo_e;
7629    /* setup context */
7630    c->obj = (Evas_Object *)eo_obj;
7631    c->o = o;
7632    c->paragraphs = c->par = NULL;
7633    c->format_stack = NULL;
7634    c->fmt = NULL;
7635    c->x = c->y = 0;
7636    c->w = w;
7637    c->h = h;
7638    c->wmax = c->hmax = 0;
7639    c->ascent = c->descent = 0;
7640    c->maxascent = c->maxdescent = 0;
7641    c->marginl = c->marginr = 0;
7642    c->have_underline = 0;
7643    c->have_underline2 = 0;
7644    c->underline_extend = 0;
7645    c->line_no = 0;
7646    c->align = 0.0;
7647    c->align_auto = EINA_TRUE;
7648    c->ln = NULL;
7649    c->width_changed = (obj->cur->geometry.w != o->last_w);
7650    c->obs_infos = NULL;
7651    c->hyphen_ti = NULL;
7652    c->handle_obstacles = EINA_FALSE;
7653    c->style_pad.r = c->style_pad.l = c->style_pad.t = c->style_pad.b = 0;
7654    c->vertical_ellipsis = EINA_FALSE;
7655    c->ellip_prev_it = NULL;
7656 
7657    /* Update all obstacles */
7658    if (c->o->obstacle_changed || c->width_changed)
7659      {
7660         _layout_obstacles_update(c);
7661         c->handle_obstacles = EINA_TRUE;
7662      }
7663 
7664    c->evas_o = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
7665    eo_e = evas_object_evas_get(eo_obj);
7666    c->evas = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
7667 
7668    /* Start of logical layout creation */
7669    /* setup default base style */
7670    if (!o->main_fmt)  /* no main format */
7671      {
7672         Eina_List *itr;
7673         User_Style_Entry *use;
7674         Eina_Bool finalize = EINA_FALSE;
7675         if (!c->fmt)
7676           {
7677              c->fmt = _layout_format_push(c, NULL, NULL);
7678              finalize = EINA_TRUE;
7679           }
7680         if ((c->o->style) && (c->o->style->default_tag))
7681           {
7682              _format_fill(c->obj, c->fmt, c->o->style->default_tag, EINA_FALSE);
7683              finalize = EINA_TRUE;
7684           }
7685 
7686         EINA_LIST_FOREACH(c->o->styles, itr, use)
7687           {
7688              if ((use->st) && (use->st->default_tag))
7689                {
7690                   _format_fill(c->obj, c->fmt, use->st->default_tag, EINA_FALSE);
7691                   finalize = EINA_TRUE;
7692                }
7693           }
7694           /* Extra Style used by fitting configure*/
7695           if (*o->fit_content_config.fit_style)
7696             {
7697                _format_fill(c->obj, c->fmt, o->fit_content_config.fit_style, EINA_FALSE);
7698             }
7699 
7700         if (finalize)
7701            _format_finalize(c->obj, c->fmt);
7702         o->main_fmt = _format_dup(c->obj, c->fmt);
7703      }
7704    else
7705      {
7706         c->fmt = _layout_format_push(c, o->main_fmt, NULL);
7707      }
7708 
7709    c->paragraphs = o->paragraphs;
7710 
7711    return EINA_TRUE;
7712 }
7713 
7714 /**
7715  * @internal
7716  * Create the layout from the nodes.
7717  *
7718  * @param obj the evas object - NOT NULL.
7719  * @param calc_only true if should only calc sizes false if should also create the layout.. It assumes native size is being calculated, doesn't support formatted size atm.
7720  * @param w the object's w, -1 means no wrapping (i.e infinite size)
7721  * @param h the object's h, -1 means inifinte size.
7722  * @param w_ret the object's calculated w.
7723  * @param h_ret the object's calculated h.
7724  */
7725 static void
_layout(const Evas_Object * eo_obj,int w,int h,int * w_ret,int * h_ret)7726 _layout(const Evas_Object *eo_obj, int w, int h, int *w_ret, int *h_ret)
7727 {
7728    Ctxt ctxt, *c;
7729    c = &ctxt;
7730 
7731    LYDBG("ZZ: layout %p %4ix%4i | w=%4i | last_w=%4i --- '%s'\n", eo_obj, w, h, obj->cur->geometry.w, o->last_w, o->markup_text);
7732 
7733    if (!_layout_setup(c, eo_obj, w, h))
7734      {
7735         if (w_ret) *w_ret = 0;
7736         if (h_ret) *h_ret = 0;
7737         return;
7738      }
7739    _layout_pre(c);
7740    _layout_visual(c);
7741    _layout_done(c, w_ret, h_ret);
7742 }
7743 
7744 /*
7745  * @internal
7746  * Relayout the object according to current object size.
7747  *
7748  * @param obj the evas object - NOT NULL.
7749  */
7750 static void
_relayout(const Evas_Object * eo_obj)7751 _relayout(const Evas_Object *eo_obj)
7752 {
7753    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
7754    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
7755    _layout(eo_obj, obj->cur->geometry.w, obj->cur->geometry.h,
7756          &o->formatted.w, &o->formatted.h);
7757    o->formatted.valid = 1;
7758    o->formatted.oneline_h = 0;
7759    o->last_w = obj->cur->geometry.w;
7760    o->wrap_changed = EINA_FALSE;
7761    LYDBG("ZZ: --------- layout %p @ %ix%i = %ix%i\n", eo_obj, obj->cur->geometry.w, obj->cur->geometry.h, o->formatted.w, o->formatted.h);
7762    o->last_h = obj->cur->geometry.h;
7763    if ((o->paragraphs) && (!EINA_INLIST_GET(o->paragraphs)->next) &&
7764        (o->paragraphs->lines) && (!EINA_INLIST_GET(o->paragraphs->lines)->next))
7765      {
7766         if (obj->cur->geometry.h < o->formatted.h + o->style_pad.t + o->style_pad.b)
7767           {
7768              LYDBG("ZZ: 1 line only... lasth == formatted h + style_pad.t + style_pad.b(%i)\n",
7769                    o->formatted.h + o->style_pad.t + o->style_pad.b);
7770              o->formatted.oneline_h = o->formatted.h + o->style_pad.t + o->style_pad.b;
7771           }
7772      }
7773    o->changed = 0;
7774    o->content_changed = 0;
7775    o->format_changed = EINA_FALSE;
7776    o->redraw = 1;
7777 #ifdef BIDI_SUPPORT
7778    o->changed_paragraph_direction = EINA_FALSE;
7779 #endif
7780 }
7781 
7782 /*
7783  * @internal
7784  * Check if the object needs a relayout, and if so, execute it.
7785  */
7786 static Eina_Bool
_relayout_if_needed(const Evas_Object * eo_obj,Efl_Canvas_Textblock_Data * o)7787 _relayout_if_needed(const Evas_Object *eo_obj, Efl_Canvas_Textblock_Data *o)
7788 {
7789    ASYNC_BLOCK;
7790    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
7791 
7792    if (obj->delete_me) return EINA_TRUE;
7793 
7794    /* XXX const */
7795    if(!fit_is_fitting(eo_obj))
7796       evas_object_textblock_coords_recalc((Evas_Object *)eo_obj, obj, obj->private_data);
7797 
7798    if (o->formatted.valid)
7799      {
7800         return EINA_TRUE;
7801      }
7802    LYDBG("ZZ: relayout\n");
7803    _relayout(eo_obj);
7804    return EINA_TRUE;
7805 }
7806 
7807 void
_evas_textblock_relayout_if_needed(Evas_Object * eo_obj)7808 _evas_textblock_relayout_if_needed(Evas_Object *eo_obj)
7809 {
7810    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
7811    _relayout_if_needed(eo_obj, o);
7812 }
7813 
7814 /**
7815  * @internal
7816  * Find the layout item and line that match the text node and position passed.
7817  *
7818  * @param obj the evas object - NOT NULL.
7819  * @param n the text node - Not null.
7820  * @param pos the position to look for - valid.
7821  * @param[out] lnr the line found - not null.
7822  * @param[out] tir the item found - not null.
7823  * @see _find_layout_format_item_line_match()
7824  */
7825 static void
_find_layout_item_line_match(Evas_Object * eo_obj,Evas_Object_Textblock_Node_Text * n,size_t pos,Evas_Object_Textblock_Line ** lnr,Evas_Object_Textblock_Item ** itr)7826 _find_layout_item_line_match(Evas_Object *eo_obj, Evas_Object_Textblock_Node_Text *n, size_t pos, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Item **itr)
7827 {
7828    Evas_Object_Textblock_Paragraph *found_par;
7829    Evas_Object_Textblock_Line *ln;
7830    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
7831 
7832    _relayout_if_needed(eo_obj, o);
7833 
7834    found_par = n->par;
7835    if (found_par)
7836      {
7837         _layout_paragraph_render(o, found_par);
7838         EINA_INLIST_FOREACH(found_par->lines, ln)
7839           {
7840              Evas_Object_Textblock_Item *it;
7841 
7842              EINA_INLIST_FOREACH(ln->items, it)
7843                {
7844                   size_t p = it->text_pos;
7845 
7846                   if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
7847                     {
7848                        Evas_Object_Textblock_Text_Item *ti =
7849                           _ITEM_TEXT(it);
7850 
7851                        p += ti->text_props.text_len;
7852                     }
7853                   else
7854                     {
7855                        p++;
7856                     }
7857 
7858                   if (((pos >= it->text_pos) && (pos < p)))
7859                     {
7860                        *lnr = ln;
7861                        *itr = it;
7862                        return;
7863                     }
7864                   else if (p == pos)
7865                     {
7866                        *lnr = ln;
7867                        *itr = it;
7868                     }
7869                }
7870           }
7871      }
7872 }
7873 
7874 /**
7875  * @internal
7876  * Return the line number 'line'.
7877  *
7878  * @param obj the evas object - NOT NULL.
7879  * @param line the line to find
7880  * @return the line of line number or NULL if no line found.
7881  */
7882 static Evas_Object_Textblock_Line *
_find_layout_line_num(const Evas_Object * eo_obj,int line)7883 _find_layout_line_num(const Evas_Object *eo_obj, int line)
7884 {
7885    Evas_Object_Textblock_Paragraph *par;
7886    Evas_Object_Textblock_Line *ln;
7887    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
7888 
7889    par = _layout_find_paragraph_by_line_no(o, line);
7890    if (par)
7891      {
7892         _layout_paragraph_render(o, par);
7893         EINA_INLIST_FOREACH(par->lines, ln)
7894           {
7895              if (par->line_no + ln->line_no == line) return ln;
7896           }
7897      }
7898    return NULL;
7899 }
7900 
7901 EAPI Evas_Object *
evas_object_textblock_add(Evas * e)7902 evas_object_textblock_add(Evas *e)
7903 {
7904    Efl_Canvas_Textblock_Data *o;
7905    MAGIC_CHECK(e, Evas, MAGIC_EVAS);
7906    return NULL;
7907    MAGIC_CHECK_END();
7908    Evas_Object *eo_obj = efl_add(MY_CLASS, e,
7909          efl_text_multiline_set(efl_added, EINA_TRUE),
7910          efl_canvas_object_legacy_ctor(efl_added));
7911    o = efl_data_scope_get(eo_obj, MY_CLASS);
7912    o->is_legacy = EINA_TRUE;
7913    o->legacy_newline = EINA_TRUE;
7914    o->auto_styles = EINA_FALSE;
7915    _FMT(password) = EINA_TRUE;
7916    return eo_obj;
7917 }
7918 
7919 EOLIAN static Eo *
_efl_canvas_textblock_efl_object_constructor(Eo * eo_obj,Efl_Canvas_Textblock_Data * class_data EINA_UNUSED)7920 _efl_canvas_textblock_efl_object_constructor(Eo *eo_obj, Efl_Canvas_Textblock_Data *class_data EINA_UNUSED)
7921 {
7922    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
7923    Efl_Canvas_Textblock_Data *o;
7924 
7925    eo_obj = efl_constructor(efl_super(eo_obj, MY_CLASS));
7926 
7927    /* set up methods (compulsory) */
7928    obj->func = &object_func;
7929    obj->private_data = efl_data_ref(eo_obj, MY_CLASS);
7930    obj->type = o_type;
7931 
7932    o = obj->private_data;
7933    o->cursor = evas_object_textblock_cursor_new(eo_obj);
7934    //XXX: empty the list hacky but we need o->cursors to not contain o->cursor
7935    o->cursors = eina_list_remove_list(o->cursors, o->cursors);
7936    _format_command_init();
7937    evas_object_textblock_init(eo_obj);
7938 
7939    _FMT(ref) = 1;
7940    _FMT(halign) = 0.0;
7941    _FMT(halign_auto) = EINA_TRUE;
7942    _FMT(valign) = -1.0;
7943    _FMT(style) = EVAS_TEXT_STYLE_PLAIN;
7944    _FMT(tabstops) = 32;
7945    _FMT(linesize) = 0;
7946    _FMT(linerelsize) = 0.0;
7947    _FMT(linegap) = 0;
7948    _FMT(underline_dash_width) = 6;
7949    _FMT(underline_dash_gap) = 2;
7950    _FMT(underline_height) = 1.0;
7951    _FMT(linerelgap) = 0.0;
7952    _FMT(password) = EINA_FALSE;
7953    _FMT(ellipsis) = -1;
7954    _FMT_INFO(bitmap_scalable) = EFL_TEXT_FONT_BITMAP_SCALABLE_COLOR;
7955 
7956    /* Fit default properties*/
7957    evas_textblock_fit_size_range_set(eo_obj,1,255);
7958    evas_textblock_fit_step_size_set(eo_obj,1);
7959 
7960    o->auto_styles = EINA_TRUE;
7961 
7962    return eo_obj;
7963 }
7964 
7965 EAPI Evas_Textblock_Style *
evas_textblock_style_new(void)7966 evas_textblock_style_new(void)
7967 {
7968    Evas_Textblock_Style *ts;
7969 
7970    ts = calloc(1, sizeof(Evas_Textblock_Style));
7971    ts->legacy = EINA_TRUE;
7972    return ts;
7973 }
7974 
7975 EAPI void
evas_textblock_style_free(Evas_Textblock_Style * ts)7976 evas_textblock_style_free(Evas_Textblock_Style *ts)
7977 {
7978    if (!ts) return;
7979    if (ts->objects)
7980      {
7981         ts->delete_me = 1;
7982         return;
7983      }
7984    _style_clear(ts);
7985    // FIXME: free up user style entry
7986    free(ts);
7987 }
7988 
7989 static void
_evas_clear_main_format(Evas_Object * eo_obj,Efl_Canvas_Textblock_Data * o)7990 _evas_clear_main_format(Evas_Object *eo_obj, Efl_Canvas_Textblock_Data *o)
7991 {
7992     if (o->main_fmt)
7993       {
7994          _format_unref_free(efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS), o->main_fmt);
7995          o->main_fmt = NULL;
7996       }
7997 }
7998 
7999 static void
_evas_textblock_update_format_nodes_from_style_tag(Evas_Object * eo_obj,Efl_Canvas_Textblock_Data * o)8000 _evas_textblock_update_format_nodes_from_style_tag(Evas_Object *eo_obj, Efl_Canvas_Textblock_Data *o)
8001 {
8002    if (!o)
8003      {
8004         ERR("The given address Efl_Canvas_Textblock_Data is NULL");
8005         return;
8006      }
8007 
8008    _evas_clear_main_format(eo_obj, o);
8009 
8010    Evas_Object_Textblock_Node_Format *fnode = o->format_nodes;
8011 
8012    while (fnode)
8013      {
8014         const char *match;
8015         size_t format_len = eina_stringshare_strlen(fnode->orig_format);
8016         /* Is this safe to use alloca here? Strings might possibly get large */
8017 
8018         if (fnode->own_closer &&
8019             (format_len > 0) && (fnode->orig_format[format_len - 1] == '/'))
8020           {
8021              format_len--;
8022           }
8023 
8024         match = _textblock_format_node_from_style_tag(o, fnode, fnode->orig_format,
8025                                                       format_len);
8026 
8027         if (match && fnode->format && strcmp(match, fnode->format))
8028           {
8029              if ((*match == '+') || (*match == '-'))
8030                {
8031                   match++;
8032                   while (*match == ' ') match++;
8033                }
8034              fnode->is_new = EINA_TRUE;
8035              eina_stringshare_replace(&fnode->format, match);
8036           }
8037 
8038         fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
8039      }
8040 }
8041 
8042 EAPI void
evas_textblock_style_set(Evas_Textblock_Style * ts,const char * text)8043 evas_textblock_style_set(Evas_Textblock_Style *ts, const char *text)
8044 {
8045    Eina_List *l;
8046    Evas_Object *eo_obj;
8047 
8048    if (!ts) return;
8049 
8050    /* If the style wasn't really changed, abort. */
8051    if ((!ts->style_text && !text) ||
8052        (ts->style_text && text && !strcmp(text, ts->style_text)))
8053       return;
8054 
8055    EINA_LIST_FOREACH(ts->objects, l, eo_obj)
8056      {
8057         Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
8058         evas_object_async_block(obj);
8059      }
8060 
8061    _style_replace(ts, text);
8062 
8063    if (ts->style_text)
8064      {
8065         // format MUST be KEY='VALUE'[KEY='VALUE']...
8066         const char *p;
8067         const char *key_start, *key_stop, *val_start;
8068 
8069         key_start = key_stop = val_start = NULL;
8070         p = ts->style_text;
8071         Eina_Strbuf *tag_value_buf = eina_strbuf_new();
8072         while (*p)
8073           {
8074              if (!key_start)
8075                {
8076                   if (!isspace((unsigned char)(*p)))
8077                     {
8078                        key_start = p;
8079                     }
8080                }
8081              else if (!key_stop)
8082                {
8083                   if ((*p == '=') || (isspace((unsigned char)(*p))))
8084                     {
8085                        key_stop = p;
8086                     }
8087                }
8088              else if (!val_start)
8089                {
8090                   if (((*p) == '\'') && (*(p + 1)))
8091                     {
8092                        val_start = ++p;
8093                     }
8094                }
8095              if ((key_start) && (key_stop) && (val_start))
8096                {
8097                   const char *tag_value = NULL;
8098                   Evas_Object_Style_Tag *tag;
8099                   const char *val_stop = NULL;
8100 
8101                   eina_strbuf_reset(tag_value_buf);
8102                   size_t tag_len;
8103                     {
8104                        val_stop = val_start;
8105                        while(*p)
8106                          {
8107                             if (*p == '\'')
8108                               {
8109                                  /* Break if we found the tag end */
8110                                  if (p[-1] != '\\')
8111                                    {
8112                                       eina_strbuf_append_length(tag_value_buf, val_stop,
8113                                             p - val_stop);
8114                                       break;
8115                                    }
8116                                  else
8117                                    {
8118                                       eina_strbuf_append_length(tag_value_buf, val_stop,
8119                                             p - val_stop - 1);
8120                                       eina_strbuf_append_char(tag_value_buf, '\'');
8121                                       val_stop = p + 1;
8122                                    }
8123                               }
8124                             p++;
8125                          }
8126                     }
8127                   /* If we didn't find an end, just aboart. */
8128                   if (!*p)
8129                     {
8130                        break;
8131                     }
8132 
8133                   tag_len = key_stop - key_start;
8134 
8135                   tag_value = eina_strbuf_string_get(tag_value_buf);
8136                   if (tag_len && (tag_value))
8137                     {
8138                        if (!strncmp(key_start, "DEFAULT", tag_len))
8139                          {
8140                             ts->default_tag = eina_stringshare_add(tag_value);
8141                          }
8142                        else
8143                          {
8144                             tag = calloc(1, sizeof(Evas_Object_Style_Tag));
8145                             if (tag)
8146                               {
8147                                  tag->tag.tag = eina_stringshare_add_length(key_start, tag_len);
8148                                  tag->tag.replace = eina_stringshare_add(tag_value);
8149                                  tag->tag.tag_len = tag_len;
8150                                  ts->tags = (Evas_Object_Style_Tag *)eina_inlist_append(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
8151                               }
8152                          }
8153                     }
8154                   key_start = key_stop = val_start = NULL;
8155                }
8156              p++;
8157           }
8158           eina_strbuf_free(tag_value_buf);
8159      }
8160 
8161    EINA_LIST_FOREACH(ts->objects, l, eo_obj)
8162      {
8163         Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
8164         _evas_textblock_update_format_nodes_from_style_tag(eo_obj, o);
8165         _evas_textblock_invalidate_all(o);
8166         _evas_textblock_changed(o, eo_obj);
8167      }
8168 }
8169 
8170 EAPI const char *
evas_textblock_style_get(const Evas_Textblock_Style * ts)8171 evas_textblock_style_get(const Evas_Textblock_Style *ts)
8172 {
8173    if (!ts) return NULL;
8174    return ts->style_text;
8175 }
8176 
8177 static const char *
_textblock_format_node_from_style_tag(Efl_Canvas_Textblock_Data * o,Evas_Object_Textblock_Node_Format * fnode,const char * format,size_t format_len)8178 _textblock_format_node_from_style_tag(Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Node_Format *fnode, const char *format, size_t format_len)
8179 {
8180    Eina_List *itr;
8181    const char *match = NULL;
8182    User_Style_Entry *use;
8183    EINA_LIST_REVERSE_FOREACH(o->styles, itr, use)
8184      {
8185         match = _style_match_tag(use->st, format, format_len);
8186         if (match)
8187            break;
8188      }
8189 
8190    if (!match)
8191      {
8192         match = _style_match_tag(o->style, format, format_len);
8193      }
8194 
8195    if (match)
8196      {
8197         if (match[0] != '-')
8198           {
8199              fnode->opener = EINA_TRUE;
8200              if (match[0] != '+')
8201                {
8202                   fnode->own_closer = EINA_TRUE;
8203                }
8204           }
8205      }
8206    return match;
8207 }
8208 
8209 /* textblock styles */
8210 
8211 static Eina_List *_style_cache = NULL;
8212 
8213 static void
_textblock_style_generic_set(Evas_Object * eo_obj,Evas_Textblock_Style * ts,const char * key)8214 _textblock_style_generic_set(Evas_Object *eo_obj, Evas_Textblock_Style *ts,
8215       const char *key)
8216 {
8217    TB_HEAD();
8218    Eina_List *itr;
8219    Evas_Textblock_Style *old_ts = NULL;
8220 
8221    _evas_clear_main_format(eo_obj, o);
8222 
8223    if (!key)
8224      {
8225         old_ts = o->style;
8226         o->style = ts;
8227      }
8228    else
8229      {
8230         User_Style_Entry *us;
8231 
8232         EINA_LIST_FOREACH(o->styles, itr, us)
8233           {
8234              if (!strcmp(us->key, key))
8235                {
8236                   //us->st = ts;
8237                   break;
8238                }
8239           }
8240         if (ts)
8241           {
8242              if (!us)
8243                {
8244                   us = calloc(1, sizeof(*us));
8245                   us->key = eina_stringshare_add(key);
8246                   o->styles = eina_list_append(o->styles, us);
8247                }
8248 
8249              old_ts = us->st;
8250              us->st = ts;
8251           }
8252         else if (us)
8253           {
8254              o->styles = eina_list_remove_list(o->styles, itr);
8255              old_ts = us->st;
8256              free(us);
8257           }
8258      }
8259 
8260    // Verify nothing has changed
8261    if (ts == old_ts) return;
8262 
8263    if ((ts) && (ts->delete_me)) return;
8264 
8265    if (old_ts)
8266      {
8267         if (o->markup_text)
8268           {
8269              eina_stringshare_del(o->markup_text);
8270              o->markup_text = NULL;
8271           }
8272 
8273         old_ts->objects = eina_list_remove(old_ts->objects, eo_obj);
8274         if (o->auto_styles && !old_ts->objects)
8275           {
8276              _style_cache = eina_list_remove(_style_cache, old_ts);
8277              evas_textblock_style_free(old_ts);
8278           }
8279         else if (!o->auto_styles && (old_ts->delete_me) && (!old_ts->objects))
8280           {
8281              // Legacy behavior ('delete_me' does not occur in new auto styles)
8282              evas_textblock_style_free(old_ts);
8283           }
8284      }
8285 
8286    if (ts)
8287      {
8288         ts->objects = eina_list_append(ts->objects, eo_obj);
8289      }
8290 
8291    _evas_textblock_update_format_nodes_from_style_tag(eo_obj, o);
8292 
8293    o->format_changed = EINA_TRUE;
8294    _evas_textblock_invalidate_all(o);
8295    _evas_textblock_changed(o, eo_obj);
8296 }
8297 
8298 EAPI void
evas_object_textblock_style_set(Eo * eo_obj,const Evas_Textblock_Style * ts)8299 evas_object_textblock_style_set(Eo *eo_obj, const Evas_Textblock_Style *ts)
8300 {
8301    EINA_SAFETY_ON_NULL_RETURN(eo_obj);
8302    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
8303    evas_object_async_block(obj);
8304    _textblock_style_generic_set(eo_obj, (Evas_Textblock_Style *) ts, NULL);
8305 }
8306 
8307 static Evas_Textblock_Style *
_style_by_key_find(Efl_Canvas_Textblock_Data * o,const char * key)8308 _style_by_key_find(Efl_Canvas_Textblock_Data *o, const char *key)
8309 {
8310    Eina_List *itr;
8311    User_Style_Entry *us;
8312 
8313    if (!key)
8314       return o->style;
8315 
8316    EINA_LIST_FOREACH(o->styles, itr, us)
8317      {
8318         if (!strcmp(us->key, key))
8319            return us->st;
8320      }
8321 
8322    return NULL;
8323 }
8324 
8325 EOLIAN static void
_efl_canvas_textblock_style_apply(Eo * eo_obj,Efl_Canvas_Textblock_Data * o,const char * style)8326 _efl_canvas_textblock_style_apply(Eo *eo_obj, Efl_Canvas_Textblock_Data *o, const char *style)
8327 {
8328    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
8329    evas_object_async_block(obj);
8330    _evas_clear_main_format(eo_obj, o);
8331    _format_fill(eo_obj, &(o->default_format.format), style, EINA_TRUE);
8332 }
8333 
8334 EAPI Evas_Textblock_Style *
evas_object_textblock_style_get(const Eo * eo_obj)8335 evas_object_textblock_style_get(const Eo *eo_obj)
8336 {
8337    EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, NULL);
8338    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
8339    evas_object_async_block(obj);
8340    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
8341    return o->style;
8342 }
8343 
8344 EOLIAN static const char *
_efl_canvas_textblock_all_styles_get(const Eo * eo_obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o)8345 _efl_canvas_textblock_all_styles_get(const Eo *eo_obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o)
8346 {
8347    if (o->default_format.default_style_str)
8348      free(o->default_format.default_style_str);
8349 
8350    o->default_format.default_style_str = _format_string_get(eo_obj, &(o->default_format.format));
8351    return o->default_format.default_style_str;
8352 }
8353 
8354 EOLIAN static Efl_Text_Cursor_Object *
_efl_canvas_textblock_cursor_create(Eo * obj,Efl_Canvas_Textblock_Data * pd EINA_UNUSED)8355 _efl_canvas_textblock_cursor_create(Eo *obj, Efl_Canvas_Textblock_Data *pd EINA_UNUSED)
8356 {
8357    Eo* cursor = efl_text_cursor_object_create(obj);
8358    efl_text_cursor_object_text_object_set(cursor, obj, obj);
8359    return cursor;
8360 }
8361 
8362 #define _STYLE_USER "_style_user"
8363 EAPI void
evas_object_textblock_style_user_push(Eo * eo_obj,Evas_Textblock_Style * ts)8364 evas_object_textblock_style_user_push(Eo *eo_obj, Evas_Textblock_Style *ts)
8365 {
8366    EINA_SAFETY_ON_NULL_RETURN(eo_obj);
8367    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
8368    evas_object_async_block(obj);
8369 
8370    _textblock_style_generic_set(eo_obj, ts, _STYLE_USER);
8371 }
8372 
8373 EAPI const Evas_Textblock_Style*
evas_object_textblock_style_user_peek(const Eo * eo_obj)8374 evas_object_textblock_style_user_peek(const Eo *eo_obj)
8375 {
8376    EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, NULL);
8377    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
8378    evas_object_async_block(obj);
8379    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
8380    Evas_Textblock_Style *ts = _style_by_key_find(o, _STYLE_USER);
8381 
8382    return ts;
8383 }
8384 
8385 EAPI void
evas_object_textblock_style_user_pop(Eo * eo_obj)8386 evas_object_textblock_style_user_pop(Eo *eo_obj)
8387 {
8388    EINA_SAFETY_ON_NULL_RETURN(eo_obj);
8389    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
8390    evas_object_async_block(obj);
8391    _textblock_style_generic_set(eo_obj, NULL, _STYLE_USER);
8392 }
8393 
8394 EAPI void
evas_object_textblock_replace_char_set(Efl_Canvas_Textblock * eo_obj,const char * ch)8395 evas_object_textblock_replace_char_set(Efl_Canvas_Textblock *eo_obj, const char *ch)
8396 {
8397    EINA_SAFETY_ON_NULL_RETURN(eo_obj);
8398    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
8399    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
8400    evas_object_async_block(obj);
8401    if (o->repch) eina_stringshare_del(o->repch);
8402    if (ch) o->repch = eina_stringshare_add(ch);
8403    else o->repch = NULL;
8404    _evas_textblock_invalidate_all(o);
8405    _evas_textblock_changed(o, eo_obj);
8406 }
8407 
8408 
8409 EOLIAN static void
_efl_canvas_textblock_newline_as_paragraph_separator_set(Eo * eo_obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o,Eina_Bool mode)8410 _efl_canvas_textblock_newline_as_paragraph_separator_set(Eo *eo_obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o, Eina_Bool mode)
8411 {
8412    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
8413    evas_object_async_block(obj);
8414    if (o->legacy_newline == mode)
8415       return;
8416 
8417    o->legacy_newline = mode;
8418    /* FIXME: Should recreate all the textnodes... For now, it's just
8419     * for new text inserted. */
8420 }
8421 
8422 EOLIAN static Eina_Bool
_efl_canvas_textblock_newline_as_paragraph_separator_get(const Eo * eo_obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o)8423 _efl_canvas_textblock_newline_as_paragraph_separator_get(const Eo *eo_obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o)
8424 {
8425    return o->legacy_newline;
8426 }
8427 
8428 EOLIAN static Eina_Bool
_efl_canvas_textblock_is_empty_get(const Eo * eo_obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o)8429 _efl_canvas_textblock_is_empty_get(const Eo *eo_obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o)
8430 {
8431    return !o->text_nodes || (eina_ustrbuf_length_get(o->text_nodes->unicode) == 0);
8432 }
8433 
8434 EAPI void
evas_object_textblock_valign_set(Efl_Canvas_Textblock * eo_obj,double align)8435 evas_object_textblock_valign_set(Efl_Canvas_Textblock *eo_obj, double align)
8436 {
8437    EINA_SAFETY_ON_NULL_RETURN(eo_obj);
8438    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
8439    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
8440    evas_object_async_block(obj);
8441    if (align < 0.0) align = 0.0;
8442    else if (align > 1.0) align = 1.0;
8443    if (EINA_DBL_EQ(o->valign, align)) return;
8444    o->valign = align;
8445    _evas_textblock_changed(o, eo_obj);
8446 }
8447 
8448 EAPI double
evas_object_textblock_valign_get(const Efl_Canvas_Textblock * obj)8449 evas_object_textblock_valign_get(const Efl_Canvas_Textblock *obj)
8450 {
8451    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, 0.0);
8452    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS);
8453    return o->valign;
8454 }
8455 
8456 EOLIAN static void
_efl_canvas_textblock_bidi_delimiters_set(Eo * eo_obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,const char * delim)8457 _efl_canvas_textblock_bidi_delimiters_set(Eo *eo_obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, const char *delim)
8458 {
8459    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
8460    evas_object_async_block(obj);
8461    eina_stringshare_replace(&o->bidi_delimiters, delim);
8462 }
8463 
8464 EOLIAN static const char*
_efl_canvas_textblock_bidi_delimiters_get(const Eo * eo_obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o)8465 _efl_canvas_textblock_bidi_delimiters_get(const Eo *eo_obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o)
8466 {
8467    return o->bidi_delimiters;
8468 }
8469 
8470 EAPI const char *
evas_object_textblock_replace_char_get(const Efl_Canvas_Textblock * obj)8471 evas_object_textblock_replace_char_get(const Efl_Canvas_Textblock *obj)
8472 {
8473    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
8474    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS);
8475    return o->repch;
8476 }
8477 
8478 /**
8479  * @internal
8480  *
8481  * @param s the escape string to search for its index
8482  * @param s_len length of s string
8483  * @param escape_values array of Escape_Value to look inside, Sorted by Escape
8484  * @param escape_values_len is the len of Escape_Value array
8485  */
8486 static int
_escaped_string_search(const char * s,size_t s_len,Escape_Value ** escape_values,const size_t escape_values_len)8487 _escaped_string_search(const char *s, size_t s_len, Escape_Value **escape_values, const size_t escape_values_len)
8488 {
8489    if (!escape_values)
8490      return -1;
8491 
8492    int l = 0;
8493    int r = escape_values_len - 1;
8494    while (l <= r)
8495      {
8496         int m = (l + r) / 2;
8497         int res = strncmp(s, escape_values[m]->escape, MAX(escape_values[m]->escape_len, s_len));
8498         if (res == 0)
8499           {
8500              //Handle special case when s_len is less than escape_len
8501              //then we will continue searching
8502              //example ("&gt;",1,....)
8503              if (escape_values[m]->escape_len > s_len)
8504                res = -1;
8505              else if (escape_values[m]->escape_len < s_len)
8506                res = 1;
8507              else return m;
8508           }
8509          if (res > 0)
8510            l = m + 1;
8511          else
8512            r = m - 1;
8513      }
8514    return -1;
8515 }
8516 
8517 /**
8518  * @internal
8519  *
8520  * @param s the value string to search for its index
8521  * @param escape_values array of Escape_Value to look inside, Sorted by Value
8522  * @param escape_values_len is the len of Escape_Value array
8523  */
8524 static int
_escaped_value_search(const char * s,Escape_Value ** escape_values,const size_t escape_values_len)8525 _escaped_value_search(const char *s, Escape_Value **escape_values , const size_t escape_values_len)
8526 {
8527    if (!escape_values)
8528      return -1;
8529 
8530    int l = 0;
8531    int r = escape_values_len - 1;
8532    while (l <= r)
8533      {
8534         int m = (l + r) / 2;
8535         int res = strncmp(s, escape_values[m]->value, escape_values[m]->value_len);
8536         if (res == 0)
8537           return m;
8538         if (res > 0)
8539           l = m + 1;
8540         else
8541           r = m - 1;
8542      }
8543    return -1;
8544 }
8545 
8546 
8547 /**
8548  * @internal
8549  *
8550  * @param s the string to match
8551  */
8552 
8553 static inline const char *
_escaped_char_match(const char * s,int * adv)8554 _escaped_char_match(const char *s, int *adv)
8555 {
8556    size_t len = 0;
8557    Escape_Value **list = get_html_escape_array_common_value_sorted(&len);
8558    int n_ret = _escaped_value_search(s, list, len);
8559    if (n_ret != -1)
8560      {
8561         if (adv)
8562           *adv = (int) list[n_ret]->value_len;
8563         return list[n_ret]->escape;
8564      }
8565    else
8566      {
8567         list = get_html_escape_array_rest_value_sorted(&len);
8568         n_ret = _escaped_value_search(s, list, len);
8569         if (n_ret != -1)
8570           {
8571              if (adv)
8572                *adv = (int)list[n_ret]->value_len;
8573              return list[n_ret]->escape;
8574           }
8575      }
8576    return NULL;
8577 }
8578 
8579 /**
8580  * @internal
8581  * FIXME: TBD.
8582  *
8583  * @param s the string to match
8584  */
8585 static inline const char *
_escaped_char_get(const char * s,const char * s_end)8586 _escaped_char_get(const char *s, const char *s_end)
8587 {
8588    /* Handle numeric escape codes. */
8589    if (s[1] == '#')
8590      {
8591         static char utf8_escape[7]; /* Support up to 6 bytes utf8 */
8592         char ustr[10];
8593         Eina_Unicode uchar[2] = { 0, 0 };
8594         char *utf8_char;
8595         size_t len = 0;
8596         int base = 10;
8597         s += 2; /* Skip "&#" */
8598 
8599         if (tolower((unsigned char)(*s)) == 'x')
8600           {
8601              s++;
8602              base = 16;
8603           }
8604 
8605         len = s_end - s;
8606         if (len > sizeof(ustr))
8607            len = sizeof(ustr);
8608 
8609         memcpy(ustr, s, len);
8610         ustr[len - 1] = '\0';
8611         uchar[0] = strtol(ustr, NULL, base);
8612 
8613         if (uchar[0] == 0)
8614           return NULL;
8615 
8616         utf8_char = eina_unicode_unicode_to_utf8(uchar, NULL);
8617         // eina_unicode_unicode_to_utf8() always creates a string that
8618         // is nul terminated - guaranteed
8619         if (utf8_char)
8620           {
8621              strcpy(utf8_escape, utf8_char);
8622              free(utf8_char);
8623           }
8624 
8625         return utf8_escape;
8626      }
8627    else
8628      {
8629         size_t len = 0;
8630         Escape_Value **list;
8631         list = get_html_escape_array_common_key_sorted(&len);
8632         int n_ret = _escaped_string_search(s, s_end-s, list, len);
8633         if (n_ret != -1)
8634           {
8635              return list[n_ret]->value;
8636           }
8637         else
8638           {
8639              list = get_html_escape_array_rest_key_sorted(&len);
8640              n_ret = _escaped_string_search(s, s_end-s, list, len);
8641              if (n_ret != -1)
8642                {
8643                   return list[n_ret]->value;
8644                }
8645           }
8646      }
8647 
8648    return NULL;
8649 }
8650 
8651 EAPI const char *
evas_textblock_escape_string_get(const char * escape)8652 evas_textblock_escape_string_get(const char *escape)
8653 {
8654    /* &amp; -> & */
8655    if (!escape) return NULL;
8656    return _escaped_char_get(escape, escape + strlen(escape));
8657 }
8658 
8659 EAPI const char *
evas_textblock_escape_string_range_get(const char * escape_start,const char * escape_end)8660 evas_textblock_escape_string_range_get(const char *escape_start, const char *escape_end)
8661 {
8662    if ((!escape_start) || (!escape_end)) return NULL;
8663    return _escaped_char_get(escape_start, escape_end);
8664 }
8665 
8666 EAPI const char *
evas_textblock_string_escape_get(const char * string,int * len_ret)8667 evas_textblock_string_escape_get(const char *string, int *len_ret)
8668 {
8669    if ((!string) || (!len_ret)) return NULL;
8670    /* & -> &amp; */
8671    return _escaped_char_match(string, len_ret);
8672 }
8673 
_evas_textblock_cursor_object_changed(Efl_Text_Cursor_Handle * cur)8674 static void _evas_textblock_cursor_object_changed(Efl_Text_Cursor_Handle *cur)
8675 {
8676    if (!cur || !cur->cur_objs) return;
8677    Eina_List *l;
8678    Eo *cur_obj;
8679 
8680    EINA_LIST_FOREACH(cur->cur_objs, l, cur_obj)
8681      efl_event_callback_call(cur_obj, EFL_TEXT_CURSOR_OBJECT_EVENT_CHANGED, NULL);
8682 }
8683 
8684 static void
_cursor_emit_if_changed(Efl_Text_Cursor_Handle * cur)8685 _cursor_emit_if_changed(Efl_Text_Cursor_Handle *cur)
8686 {
8687    if (cur->changed)
8688      {
8689         cur->changed = EINA_FALSE;
8690         _evas_textblock_cursor_object_changed(cur);
8691      }
8692 }
8693 
8694 /**
8695  * @internal
8696  * Prepends the escaped char between s and s_end to the cursor
8697  *
8698  *
8699  * @param s the start of the string
8700  * @param s_end the end of the string.
8701  */
8702 static inline void
_prepend_escaped_char(Efl_Text_Cursor_Handle * cur_obj,const char * s,const char * s_end)8703 _prepend_escaped_char(Efl_Text_Cursor_Handle *cur_obj, const char *s,
8704       const char *s_end)
8705 {
8706    const char *escape;
8707 
8708    escape = _escaped_char_get(s, s_end);
8709    if (escape)
8710      evas_textblock_cursor_text_prepend(cur_obj, escape);
8711    else /* Use same text input if no escape was found */
8712      _prepend_text_run(cur_obj, s, s_end);
8713 }
8714 
8715 
8716 static void
_evas_object_textblock_text_markup_set(Eo * eo_obj,Efl_Canvas_Textblock_Data * o,const char * text)8717 _evas_object_textblock_text_markup_set(Eo *eo_obj, Efl_Canvas_Textblock_Data *o,
8718       const char *text)
8719 {
8720    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
8721    evas_object_async_block(obj);
8722    if (text == o->markup_text)
8723      {
8724         /* Text is the same and already stringshared, do nothing */
8725         return;
8726      }
8727    else
8728      {
8729         text = eina_stringshare_add(text);
8730         if (text == o->markup_text)
8731           {
8732              eina_stringshare_del(text);
8733              /* Text is the same, do nothing. */
8734              return;
8735           }
8736      }
8737 
8738    if (o->paragraphs)
8739      {
8740         _paragraphs_free(o, obj, o->paragraphs);
8741         o->paragraphs = NULL;
8742      }
8743    _nodes_clear(eo_obj);
8744 
8745    if (o->cursor->pos != 0)
8746    {
8747       o->cursor->changed = EINA_TRUE;
8748       o->cursor->pos = 0;
8749    }
8750 
8751    o->text_nodes = _NODE_TEXT(eina_inlist_append(
8752             EINA_INLIST_GET(o->text_nodes),
8753             EINA_INLIST_GET(_evas_textblock_node_text_new())));
8754    o->cursor->node = o->text_nodes;
8755 
8756    evas_object_textblock_text_markup_prepend(o->cursor, text);
8757 
8758    Eina_List *l;
8759    Efl_Text_Cursor_Handle *cur;
8760    EINA_LIST_FOREACH(o->cursors, l, cur)
8761      {
8762         cur->node = o->text_nodes;
8763         if (cur->pos != 0)
8764           {
8765              cur->pos = 0;
8766              cur->changed = EINA_TRUE;
8767           }
8768      }
8769    _cursor_emit_if_changed(o->cursor);
8770    EINA_LIST_FOREACH(o->cursors, l, cur)
8771      {
8772         _cursor_emit_if_changed(cur);
8773      }
8774 
8775    /*If there was no text markup_prepend will not call change function
8776      So we will call it inside markup_set*/
8777    if (!text || !*text)
8778      {
8779         efl_event_callback_call(eo_obj, EFL_CANVAS_TEXTBLOCK_EVENT_CHANGED, NULL);
8780         _evas_textblock_changed(o, eo_obj);
8781      }
8782 
8783     o->markup_text = text;
8784 }
8785 
8786 EAPI void
evas_object_textblock_text_markup_set(Eo * eo_obj,const char * text)8787 evas_object_textblock_text_markup_set(Eo *eo_obj, const char *text)
8788 {
8789    EINA_SAFETY_ON_NULL_RETURN(eo_obj);
8790    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
8791    _evas_object_textblock_text_markup_set(eo_obj, o, text);
8792 }
8793 
8794 EOLIAN void
_efl_canvas_textblock_efl_text_markup_markup_set(Eo * eo_obj,Efl_Canvas_Textblock_Data * o,const char * text)8795 _efl_canvas_textblock_efl_text_markup_markup_set(Eo *eo_obj, Efl_Canvas_Textblock_Data *o,
8796       const char *text)
8797 {
8798    ASYNC_BLOCK;
8799    _evas_object_textblock_text_markup_set(eo_obj, o, text);
8800 }
8801 
8802 static void
_evas_object_textblock_text_markup_prepend(Eo * eo_obj,Efl_Text_Cursor_Handle * cur,const char * text)8803 _evas_object_textblock_text_markup_prepend(Eo *eo_obj,
8804       Efl_Text_Cursor_Handle *cur, const char *text)
8805 {
8806    if (!cur || !text || !*text) return;
8807    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
8808    evas_object_async_block(obj);
8809    TB_HEAD();
8810 
8811    /* Stop calls for _evas_textblock_changed for each cursor_text_append or cursor_format_append
8812     * this should be done once, when markup_prepend finished */
8813    o->pause_change = EINA_TRUE;
8814 
8815    char *s, *p;
8816    char *tag_start, *tag_end, *esc_start, *esc_end;
8817 
8818    tag_start = tag_end = esc_start = esc_end = NULL;
8819    p = (char *)text;
8820    s = p;
8821    /* This loop goes through all of the mark up text until it finds format
8822     * tags, escape sequences or the terminating NULL. When it finds either
8823     * of those, it appends the text found up until that point to the textblock
8824     * proccesses whatever found. It repeats itself until the terminating
8825     * NULL is reached. */
8826    for (;;)
8827      {
8828         size_t text_len;
8829         /* If we got to the end of string or just finished/started tag
8830          * or escape sequence handling. */
8831         if ((*p == 0) ||
8832               (tag_end) || (esc_end) ||
8833               (tag_start) || (esc_start))
8834           {
8835              if (tag_end)
8836                {
8837                   /* If we reached to a tag ending, analyze the tag */
8838                   char *ttag;
8839                   size_t ttag_len = tag_end - tag_start;
8840 
8841 
8842                   ttag = malloc(ttag_len + 1);
8843                   if (ttag)
8844                     {
8845                        memcpy(ttag, tag_start, ttag_len);
8846                        ttag[ttag_len] = 0;
8847                        evas_textblock_cursor_format_prepend(cur, ttag);
8848                        free(ttag);
8849                     }
8850                   tag_start = tag_end = NULL;
8851                }
8852              else if (esc_end)
8853                {
8854                   _prepend_escaped_char(cur, esc_start, esc_end + 1);
8855                   esc_start = esc_end = NULL;
8856                }
8857              else if (*p == 0 && esc_start) /* escape start with no end, append it as text */
8858                {
8859                   _prepend_text_run(cur, esc_start, p);
8860                   esc_start = esc_end = NULL;
8861                   s = NULL;
8862                }
8863              else if (*p == 0)
8864                {
8865                   _prepend_text_run(cur, s, p);
8866                   s = NULL;
8867                }
8868              if (*p == 0)
8869                break;
8870           }
8871         if (*p == '<')
8872           {
8873              if (esc_start) /* escape start with no end, append it as text */
8874                {
8875                   _prepend_text_run(cur, esc_start, p);
8876                   esc_start = esc_end = NULL;
8877                   s = NULL;
8878                }
8879              if (!esc_start)
8880                {
8881                   /* Append the text prior to this to the textblock and mark
8882                    * the start of the tag */
8883                   tag_start = p;
8884                   tag_end = NULL;
8885                   _prepend_text_run(cur, s, p);
8886                   s = NULL;
8887                }
8888           }
8889         else if (*p == '>')
8890           {
8891              if (tag_start)
8892                {
8893                   tag_end = p + 1;
8894                   s = p + 1;
8895                }
8896           }
8897         else if (*p == '&')
8898           {
8899              if (esc_start) /* escape start with no end, append it as text */
8900                {
8901                   _prepend_text_run(cur, esc_start, p);
8902                   esc_start = esc_end = NULL;
8903                   s = NULL;
8904                }
8905              if (!tag_start)
8906                {
8907                   /* Append the text prior to this to the textblock and mark
8908                    * the start of the escape sequence */
8909                   esc_start = p;
8910                   esc_end = NULL;
8911                   _prepend_text_run(cur, s, p);
8912                   s = NULL;
8913                }
8914           }
8915         else if (*p == ';')
8916           {
8917              if (esc_start)
8918                {
8919                   esc_end = p;
8920                   s = p + 1;
8921                }
8922           }
8923         /* Unicode object replacement char */
8924         else if (!strncmp(_REPLACEMENT_CHAR_UTF8, p,
8925                  text_len = strlen(_REPLACEMENT_CHAR_UTF8)) ||
8926               !strncmp(_NEWLINE_UTF8, p,
8927                  text_len = strlen(_NEWLINE_UTF8)) ||
8928               !strncmp(_TAB_UTF8, p,
8929                  text_len = strlen(_TAB_UTF8)) ||
8930               !strncmp(_PARAGRAPH_SEPARATOR_UTF8, p,
8931                  text_len = strlen(_PARAGRAPH_SEPARATOR_UTF8)))
8932           {
8933              /*FIXME: currently just remove them, maybe do something
8934               * fancier in the future, atm it breaks if this char
8935               * is inside <> */
8936              _prepend_text_run(cur, s, p);
8937              /* it's also advanced later in this loop need +text_len
8938                 in total*/
8939              p += text_len - 1;
8940              s = p + 1; /* One after the end of the replacement char */
8941           }
8942         p++;
8943      }
8944 
8945    o->pause_change = EINA_FALSE;
8946    efl_event_callback_call(cur->obj, EFL_CANVAS_TEXTBLOCK_EVENT_CHANGED, NULL);
8947    _evas_textblock_changed(o, eo_obj);
8948 }
8949 
8950 EAPI void
evas_object_textblock_text_markup_prepend(Efl_Text_Cursor_Handle * cur,const char * text)8951 evas_object_textblock_text_markup_prepend(Efl_Text_Cursor_Handle *cur, const char *text)
8952 {
8953    EINA_SAFETY_ON_NULL_RETURN(cur);
8954    _evas_object_textblock_text_markup_prepend(cur->obj, cur, text);
8955 }
8956 
8957 
8958 /**
8959  * @internal
8960  * An helper function to markup get. Appends the format from fnode to the strbugf txt.
8961  *
8962  * @param o the textblock object.
8963  * @param txt the strbuf to append to.
8964  * @param fnode the format node to process.
8965  */
8966 static void
_markup_get_format_append(Eina_Strbuf * txt,Evas_Object_Textblock_Node_Format * fnode)8967 _markup_get_format_append(Eina_Strbuf *txt, Evas_Object_Textblock_Node_Format *fnode)
8968 {
8969    eina_strbuf_append_char(txt, '<');
8970      {
8971         const char *s;
8972 
8973         // FIXME: need to escape
8974         s = fnode->orig_format;
8975         eina_strbuf_append(txt, s);
8976      }
8977    eina_strbuf_append_char(txt, '>');
8978 }
8979 
8980 /**
8981  * @internal
8982  * An helper function to _markup_get_text_append and others, used for getting
8983  * back only the "dangerous" escapes.
8984  */
8985 static void
_markup_get_text_utf8_append(Eina_Strbuf * sbuf,const char * text)8986 _markup_get_text_utf8_append(Eina_Strbuf *sbuf, const char *text)
8987 {
8988    int ch, pos = 0, pos2 = 0;
8989    const char * replacement;
8990 
8991    for (;;)
8992      {
8993         pos = pos2;
8994         ch = eina_unicode_utf8_next_get(text, &pos2);
8995         if ((ch <= 0) || (pos2 <= 0)) break;
8996 
8997         if (ch == _NEWLINE)
8998            eina_strbuf_append(sbuf, "<br/>");
8999         else if (ch == _TAB)
9000            eina_strbuf_append(sbuf, "<tab/>");
9001         else if (ch == _REPLACEMENT_CHAR)
9002            eina_strbuf_append(sbuf, "&#xfffc;");
9003         else if (ch == _PARAGRAPH_SEPARATOR)
9004            eina_strbuf_append(sbuf, "<ps/>");
9005         else
9006           {
9007              replacement = _escaped_char_match(text + pos, NULL);
9008              if (replacement)
9009                {
9010                   eina_strbuf_append(sbuf, replacement);
9011                }
9012              else if (ch != '\r')
9013                {
9014                   eina_strbuf_append_length(sbuf, text + pos, pos2 - pos);
9015                }
9016           }
9017      }
9018 }
9019 
9020 /**
9021  * @internal
9022  * An helper function to markup get. Appends the text in text.
9023  *
9024  * @param txt the strbuf to append to.
9025  * @param text the text to process.
9026  */
9027 static void
_markup_get_text_append(Eina_Strbuf * txt,const Eina_Unicode * text)9028 _markup_get_text_append(Eina_Strbuf *txt, const Eina_Unicode *text)
9029 {
9030    char *base = eina_unicode_unicode_to_utf8(text, NULL);
9031 
9032    if (!base) return;
9033 
9034    _markup_get_text_utf8_append(txt, base);
9035 
9036    free(base);
9037 }
9038 static const char*
_evas_object_textblock_text_markup_get(const Eo * eo_obj,Efl_Canvas_Textblock_Data * o)9039 _evas_object_textblock_text_markup_get(const Eo *eo_obj, Efl_Canvas_Textblock_Data *o)
9040 {
9041    Evas_Object_Textblock_Node_Text *n;
9042    Eina_Strbuf *txt = NULL;
9043 
9044    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
9045    evas_object_async_block(obj);
9046    const char *markup;
9047    if (o->markup_text)
9048      {
9049         markup = (o->markup_text);
9050         return markup;
9051      }
9052    txt = eina_strbuf_new();
9053    EINA_INLIST_FOREACH(o->text_nodes, n)
9054      {
9055         Evas_Object_Textblock_Node_Format *fnode;
9056         Eina_Unicode *text_base, *text;
9057         int off;
9058         int len;
9059 
9060         /* For each text node to thorugh all of it's format nodes
9061          * append text from the start to the offset of the next format
9062          * using the last format got. If needed it also creates format items
9063          * this is the core algorithm of the layout mechanism.
9064          * Skip the unicode replacement chars when there are because
9065          * we don't want to print them. */
9066         len = (int) eina_ustrbuf_length_get(n->unicode);
9067         text_base = text =
9068            eina_unicode_strndup(eina_ustrbuf_string_get(n->unicode), len);
9069         fnode = n->format_node;
9070         off = 0;
9071         while (fnode && (fnode->text_node == n))
9072           {
9073              Eina_Unicode tmp_ch;
9074              off += fnode->offset;
9075 
9076              if (off > len) break;
9077              /* No need to skip on the first run */
9078              tmp_ch = text[off];
9079              text[off] = 0; /* Null terminate the part of the string */
9080              _markup_get_text_append(txt, text);
9081              _markup_get_format_append(txt, fnode);
9082              text[off] = tmp_ch; /* Restore the char */
9083              text += off;
9084              if (fnode->visible)
9085                {
9086                   off = -1;
9087                   text++;
9088                }
9089              else
9090                {
9091                   off = 0;
9092                }
9093              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
9094           }
9095         /* Add the rest, skip replacement */
9096         _markup_get_text_append(txt, text);
9097         free(text_base);
9098      }
9099 
9100    (((Efl_Canvas_Textblock_Data *)o)->markup_text) = eina_stringshare_add(eina_strbuf_string_get(txt));
9101    eina_strbuf_free(txt);
9102    markup = (o->markup_text);
9103 
9104    return markup;
9105 }
9106 
9107 EAPI const char*
evas_object_textblock_text_markup_get(Eo * eo_obj)9108 evas_object_textblock_text_markup_get(Eo *eo_obj)
9109 {
9110    EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, NULL);
9111    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
9112    return _evas_object_textblock_text_markup_get(eo_obj, o);
9113 }
9114 
9115 EOLIAN const char*
_efl_canvas_textblock_efl_text_markup_markup_get(const Eo * eo_obj,Efl_Canvas_Textblock_Data * o)9116 _efl_canvas_textblock_efl_text_markup_markup_get(const Eo *eo_obj, Efl_Canvas_Textblock_Data *o)
9117 {
9118    return _evas_object_textblock_text_markup_get(eo_obj, o);
9119 }
9120 
9121 EAPI char *
evas_textblock_text_markup_to_utf8(const Evas_Object * eo_obj,const char * text)9122 evas_textblock_text_markup_to_utf8(const Evas_Object *eo_obj, const char *text)
9123 {
9124    /* FIXME: Redundant and awful, should be merged with markup_prepend */
9125    Eina_Strbuf *sbuf;
9126    char *s, *p, *ret;
9127    char *tag_start, *tag_end, *esc_start, *esc_end;
9128 
9129    if (!text) return NULL;
9130 
9131    tag_start = tag_end = esc_start = esc_end = NULL;
9132    sbuf = eina_strbuf_new();
9133    p = (char *)text;
9134    s = p;
9135    /* This loop goes through all of the mark up text until it finds format
9136     * tags, escape sequences or the terminating NULL. When it finds either
9137     * of those, it appends the text found up until that point to the textblock
9138     * proccesses whatever found. It repeats itself until the terminating
9139     * NULL is reached. */
9140    for (;;)
9141      {
9142         /* If we got to the end of string or just finished/started tag
9143          * or escape sequence handling. */
9144         if ((*p == 0) ||
9145               (tag_end) || (esc_end) ||
9146               (tag_start) || (esc_start))
9147           {
9148              if (tag_end)
9149                {
9150                   /* If we reached to a tag ending, analyze the tag */
9151                   char *ttag;
9152                   size_t ttag_len;
9153 
9154                   tag_start++; /* Skip the < */
9155                   tag_end--; /* Skip the > */
9156                   if ((tag_end > tag_start) && (*(tag_end - 1) == '/'))
9157                     {
9158                        tag_end --; /* Skip the terminating '/' */
9159                        while (*(tag_end - 1) == ' ')
9160                          tag_end--; /* skip trailing ' ' */
9161                     }
9162 
9163                   ttag_len = tag_end - tag_start;
9164 
9165                   ttag = malloc(ttag_len + 1);
9166                   if (ttag)
9167                     {
9168                        const char *match = NULL;
9169                        memcpy(ttag, tag_start, ttag_len);
9170                        ttag[ttag_len] = 0;
9171 
9172 
9173                        if (eo_obj)
9174                          {
9175                             match = _style_match_tag(
9176                                   evas_object_textblock_style_get(eo_obj),
9177                                   ttag, ttag_len);
9178                          }
9179 
9180                        if (!match) match = ttag;
9181 
9182                        if (_IS_PARAGRAPH_SEPARATOR_SIMPLE(match))
9183                           eina_strbuf_append(sbuf, _PARAGRAPH_SEPARATOR_UTF8);
9184                        else if (_IS_LINE_SEPARATOR(match))
9185                           eina_strbuf_append(sbuf, _NEWLINE_UTF8);
9186                        else if (_IS_TAB(match))
9187                           eina_strbuf_append(sbuf, _TAB_UTF8);
9188                        else if (!strncmp(match, "item", 4))
9189                           eina_strbuf_append(sbuf, _REPLACEMENT_CHAR_UTF8);
9190 
9191                        free(ttag);
9192                     }
9193                   tag_start = tag_end = NULL;
9194                }
9195              else if (esc_end)
9196                {
9197                   const char *escape;
9198 
9199                   escape = _escaped_char_get(esc_start, esc_end + 1);
9200                   if (escape) eina_strbuf_append(sbuf, escape);
9201                   esc_start = esc_end = NULL;
9202                }
9203              else if (*p == 0)
9204                {
9205                   if (s)
9206                     {
9207                        eina_strbuf_append_length(sbuf, s, p - s);
9208                        s = NULL;
9209                     }
9210                   else
9211                     {
9212                        ERR("There is a invalid markup tag at position '%u'. Please check the text.", (unsigned int) (p - text));
9213                     }
9214                }
9215              if (*p == 0)
9216                 break;
9217           }
9218         if (*p == '<')
9219           {
9220              if (!esc_start)
9221                {
9222                   /* Append the text prior to this to the textblock and
9223                    * mark the start of the tag */
9224                   tag_start = p;
9225                   tag_end = NULL;
9226                   if (s)
9227                     {
9228                        eina_strbuf_append_length(sbuf, s, p - s);
9229                        s = NULL;
9230                     }
9231                   else
9232                     {
9233                        ERR("There is a invalid markup tag at position '%u'. Please check the text.", (unsigned int) (p - text));
9234                     }
9235                }
9236           }
9237         else if (*p == '>')
9238           {
9239              if (tag_start)
9240                {
9241                   tag_end = p + 1;
9242                   s = p + 1;
9243                }
9244           }
9245         else if (*p == '&')
9246           {
9247              if (!tag_start)
9248                {
9249                   /* Append the text prior to this to the textblock and mark
9250                    * the start of the escape sequence */
9251                   esc_start = p;
9252                   esc_end = NULL;
9253                   if (s)
9254                     {
9255                        eina_strbuf_append_length(sbuf, s, p - s);
9256                        s = NULL;
9257                     }
9258                   else
9259                     {
9260                        ERR("There is a invalid markup tag at position '%u'. Please check the text.", (unsigned int) (p - text));
9261                     }
9262                }
9263           }
9264         else if (*p == ';')
9265           {
9266              if (esc_start)
9267                {
9268                   esc_end = p;
9269                   s = p + 1;
9270                }
9271           }
9272         p++;
9273      }
9274 
9275    ret = eina_strbuf_string_steal(sbuf);
9276    eina_strbuf_free(sbuf);
9277    return ret;
9278 }
9279 
9280 EAPI char *
evas_textblock_text_utf8_to_markup(const Evas_Object * eo_obj EINA_UNUSED,const char * text)9281 evas_textblock_text_utf8_to_markup(const Evas_Object *eo_obj EINA_UNUSED,
9282       const char *text)
9283 {
9284    Eina_Strbuf *sbuf;
9285    char *str = NULL;
9286 
9287    if (!text) return NULL;
9288 
9289    sbuf = eina_strbuf_new();
9290 
9291    _markup_get_text_utf8_append(sbuf, text);
9292 
9293    str = eina_strbuf_string_steal(sbuf);
9294    eina_strbuf_free(sbuf);
9295    return str;
9296 }
9297 
9298 static void
_obstacle_update(Evas_Textblock_Obstacle * obs,Eo * eo_obj)9299 _obstacle_update(Evas_Textblock_Obstacle *obs, Eo *eo_obj)
9300 {
9301    Eina_Rect tb_geom, obs_geom;
9302    Eo *eo_obs = obs->eo_obs;
9303 
9304    obs_geom = efl_gfx_entity_geometry_get(eo_obs);
9305    tb_geom = efl_gfx_entity_geometry_get(eo_obj);
9306 
9307    obs->x = obs_geom.x - tb_geom.x;
9308    obs->y = obs_geom.y - tb_geom.y;
9309    obs->w = obs_geom.w;
9310    obs->h = obs_geom.h;
9311 }
9312 
9313 static void
_layout_obstacles_update(Ctxt * c)9314 _layout_obstacles_update(Ctxt *c)
9315 {
9316    Eina_List *i;
9317    Eina_Bool obstacle_changed = c->o->obstacle_changed;
9318    Evas_Textblock_Obstacle *obs;
9319 
9320    EINA_LIST_FOREACH(c->o->obstacles, i, obs)
9321      {
9322         if (obstacle_changed)
9323            _obstacle_update(obs, c->obj);
9324      }
9325 }
9326 
9327 static Evas_Textblock_Obstacle *
_obstacle_find(Efl_Canvas_Textblock_Data * o,Eo * eo_obs)9328 _obstacle_find(Efl_Canvas_Textblock_Data *o, Eo *eo_obs)
9329 {
9330    Evas_Textblock_Obstacle *obs;
9331    Eina_List *i;
9332 
9333    EINA_LIST_FOREACH(o->obstacles, i, obs)
9334      {
9335         if (eo_obs == obs->eo_obs)
9336            return obs;
9337      }
9338    return NULL;
9339 }
9340 
9341 void
_obstacle_del_cb(void * data,const Efl_Event * event)9342 _obstacle_del_cb(void *data, const Efl_Event *event)
9343 {
9344    Eo *eo_obj = data;
9345    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
9346    Eina_List *i;
9347    Evas_Textblock_Obstacle *obs;
9348 
9349    EINA_LIST_FOREACH(o->obstacles, i, obs)
9350      {
9351         if (event->object == obs->eo_obs)
9352            break;
9353      }
9354    o->obstacles = eina_list_remove_list(o->obstacles, i);
9355    free(obs);
9356    _evas_textblock_changed(o, data);
9357    o->obstacle_changed = EINA_TRUE;
9358 }
9359 
9360 static void
_obstacle_clear(Eo * eo_obj,Evas_Textblock_Obstacle * obs)9361 _obstacle_clear(Eo *eo_obj, Evas_Textblock_Obstacle *obs)
9362 {
9363    efl_event_callback_del(obs->eo_obs, EFL_EVENT_DEL, _obstacle_del_cb, eo_obj);
9364 }
9365 
9366 static void
_obstacle_free(Eo * eo_obj,Evas_Textblock_Obstacle * obs)9367 _obstacle_free(Eo *eo_obj, Evas_Textblock_Obstacle *obs)
9368 {
9369    _obstacle_clear(eo_obj, obs);
9370    free(obs);
9371 }
9372 
9373 static void
_obstacles_free(Eo * eo_obj,Efl_Canvas_Textblock_Data * o)9374 _obstacles_free(Eo *eo_obj, Efl_Canvas_Textblock_Data *o)
9375 {
9376    Evas_Textblock_Obstacle *obs;
9377 
9378    EINA_LIST_FREE(o->obstacles, obs)
9379      {
9380         _obstacle_free(eo_obj, obs);
9381      }
9382 }
9383 
9384 EOLIAN static Eina_Bool
_efl_canvas_textblock_obstacle_add(Eo * eo_obj,Efl_Canvas_Textblock_Data * o,Eo * eo_obs)9385 _efl_canvas_textblock_obstacle_add(Eo *eo_obj,
9386       Efl_Canvas_Textblock_Data *o, Eo *eo_obs)
9387 {
9388    Evas_Textblock_Obstacle *obs;
9389 
9390    if (!efl_isa(eo_obs, EFL_CANVAS_OBJECT_CLASS))
9391       return EINA_FALSE;
9392    obs = _obstacle_find(o, eo_obs);
9393    if (obs) return EINA_FALSE;
9394 
9395    obs = calloc(1, sizeof(Evas_Textblock_Obstacle));
9396    if (!obs) return EINA_FALSE;
9397 
9398    obs->eo_obs = eo_obs;
9399    efl_event_callback_add(eo_obs, EFL_EVENT_DEL, _obstacle_del_cb, eo_obj);
9400 
9401    o->obstacles = eina_list_append(o->obstacles, obs);
9402    _obstacle_update(obs, eo_obj);
9403    _evas_textblock_changed(o, eo_obj);
9404    o->obstacle_changed = EINA_TRUE;
9405    return EINA_TRUE;
9406 }
9407 
9408 EOLIAN static Eina_Bool
_efl_canvas_textblock_obstacle_del(Eo * eo_obj,Efl_Canvas_Textblock_Data * o,Eo * eo_obs EINA_UNUSED)9409 _efl_canvas_textblock_obstacle_del(Eo *eo_obj, Efl_Canvas_Textblock_Data *o,
9410       Eo *eo_obs EINA_UNUSED)
9411 {
9412    Evas_Textblock_Obstacle *obs;
9413    Eina_List *i;
9414 
9415    if (!efl_isa(eo_obs, EFL_CANVAS_OBJECT_CLASS))
9416       return EINA_FALSE;
9417 
9418    EINA_LIST_FOREACH(o->obstacles, i, obs)
9419      {
9420         if (eo_obs == obs->eo_obs)
9421           {
9422              break;
9423           }
9424      }
9425    if (!i) return EINA_FALSE;
9426    o->obstacles = eina_list_remove_list(o->obstacles, i);
9427    _obstacle_free(eo_obj, obs);
9428    _evas_textblock_changed(o, eo_obj);
9429    o->obstacle_changed = EINA_TRUE;
9430    return EINA_TRUE;
9431 }
9432 
9433 EOLIAN static void
_efl_canvas_textblock_obstacles_update(Eo * eo_obj,Efl_Canvas_Textblock_Data * obj)9434 _efl_canvas_textblock_obstacles_update(Eo *eo_obj, Efl_Canvas_Textblock_Data *obj)
9435 {
9436    _evas_textblock_changed(obj, eo_obj);
9437    obj->obstacle_changed = EINA_TRUE;
9438 }
9439 
9440 static Evas_Textblock_Obstacle *
_layout_item_obstacle_get(Ctxt * c,Evas_Object_Textblock_Item * it)9441 _layout_item_obstacle_get(Ctxt *c, Evas_Object_Textblock_Item *it)
9442 {
9443    Evas_Textblock_Obstacle *obs, *min_obs = NULL;
9444    Eina_List *i;
9445 
9446    EINA_LIST_FOREACH(c->o->obstacles, i, obs)
9447      {
9448         Eina_Bool is_visible;
9449         is_visible = efl_gfx_entity_visible_get(obs->eo_obs);
9450         if (!is_visible)
9451            continue;
9452         if ((obs->y < c->y + it->h) &&
9453             (obs->x < c->x + it->w) &&
9454             (obs->x + obs->w > c->x) &&
9455             (obs->y + obs->h > c->y))
9456           {
9457              if ((obs->x < c->w) &&
9458                    (!min_obs || (obs->x < min_obs->x)))
9459                {
9460                   min_obs = obs;
9461                }
9462           }
9463      }
9464    return min_obs;
9465 }
9466 
9467 /* Hyphenation (since 1.17) */
9468 static Evas_Object_Textblock_Text_Item *
_layout_hyphen_item_new(Ctxt * c,const Evas_Object_Textblock_Text_Item * cur_ti)9469 _layout_hyphen_item_new(Ctxt *c, const Evas_Object_Textblock_Text_Item *cur_ti)
9470 {
9471    /* U+2010 - Unicode HYPHEN */
9472    const Eina_Unicode _hyphen_str[2] = { 0x2010, '\0' };
9473    Evas_Object_Textblock_Text_Item *hyphen_ti;
9474    Evas_Script_Type script;
9475    Evas_Font_Instance *script_fi = NULL, *cur_fi;
9476    size_t len = 1; /* The length of _hyphen_str */
9477 
9478    if (c->hyphen_ti)
9479      {
9480         _item_free(c->o, c->evas_o, NULL, _ITEM(c->hyphen_ti));
9481      }
9482    c->hyphen_ti = hyphen_ti = _layout_text_item_new(c, cur_ti->parent.format);
9483    hyphen_ti->parent.text_node = cur_ti->parent.text_node;
9484    hyphen_ti->parent.text_pos = cur_ti->parent.text_pos + cur_ti->text_props.text_len - 1;
9485    script = evas_common_language_script_type_get(_hyphen_str, len);
9486 
9487    evas_common_text_props_bidi_set(&hyphen_ti->text_props,
9488          c->par->bidi_props, hyphen_ti->parent.text_pos);
9489    evas_common_text_props_script_set (&hyphen_ti->text_props, script);
9490 
9491    if (hyphen_ti->parent.format->font.font)
9492      {
9493         Evas_Object_Protected_Data *obj = c->evas_o;
9494         /* It's only 1 char anyway, we don't need the run end. */
9495         (void) ENFN->font_run_end_get(ENC,
9496               hyphen_ti->parent.format->font.font, &script_fi, &cur_fi,
9497               script, _hyphen_str, len);
9498 
9499         ENFN->font_text_props_info_create(ENC,
9500               cur_fi, _hyphen_str, &hyphen_ti->text_props,
9501               c->par->bidi_props, hyphen_ti->parent.text_pos, len, EVAS_TEXT_PROPS_MODE_SHAPE,
9502               hyphen_ti->parent.format->font.fdesc->lang);
9503      }
9504 
9505    _text_item_update_sizes(c, hyphen_ti);
9506    return hyphen_ti;
9507 }
9508 
9509 /* cursors */
9510 
9511 /**
9512  * @internal
9513  * Merge the current node with the next, no need to remove PS, already
9514  * not there.
9515  *
9516  * @param o the text block object.
9517  * @param to merge into to.
9518  */
9519 static void
_evas_textblock_nodes_merge(Efl_Canvas_Textblock_Data * o,Evas_Object_Textblock_Node_Text * to)9520 _evas_textblock_nodes_merge(Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Node_Text *to)
9521 {
9522    Evas_Object_Textblock_Node_Format *itr;
9523    Evas_Object_Textblock_Node_Format *pnode;
9524    Evas_Object_Textblock_Node_Text *from;
9525    const Eina_Unicode *text;
9526    int to_len, len;
9527 
9528    if (!to) return;
9529    from = _NODE_TEXT(EINA_INLIST_GET(to)->next);
9530 
9531    to_len = eina_ustrbuf_length_get(to->unicode);
9532    text = eina_ustrbuf_string_get(from->unicode);
9533    len = eina_ustrbuf_length_get(from->unicode);
9534    eina_ustrbuf_append_length(to->unicode, text, len);
9535 
9536    itr = from->format_node;
9537    if (itr && (itr->text_node == from))
9538      {
9539         pnode = _NODE_FORMAT(EINA_INLIST_GET(itr)->prev);
9540         if (pnode && (pnode->text_node == to))
9541           {
9542              itr->offset += to_len - _evas_textblock_node_format_pos_get(pnode);
9543           }
9544         else
9545           {
9546              itr->offset += to_len;
9547           }
9548      }
9549 
9550    while (itr && (itr->text_node == from))
9551      {
9552         itr->text_node = to;
9553         itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
9554      }
9555    if (!to->format_node || (to->format_node->text_node != to))
9556      {
9557         to->format_node = from->format_node;
9558      }
9559 
9560    /* When it comes to how we handle it, merging is like removing both nodes
9561     * and creating a new one, so we need to do the needed cleanups. */
9562    if (to->par)
9563       to->par->text_node = NULL;
9564    to->par = NULL;
9565 
9566    to->is_new = EINA_TRUE;
9567 
9568    _evas_textblock_cursors_set_node(o, from, to);
9569    _evas_textblock_node_text_remove(o, from);
9570 }
9571 
9572 /**
9573  * @internal
9574  * Merge the current node with the next, no need to remove PS, already
9575  * not there.
9576  *
9577  * @param cur the cursor that points to the current node
9578  */
9579 static void
_evas_textblock_cursor_nodes_merge(Efl_Text_Cursor_Handle * cur)9580 _evas_textblock_cursor_nodes_merge(Efl_Text_Cursor_Handle *cur)
9581 {
9582    Evas_Object_Textblock_Node_Text *nnode;
9583    int len;
9584    if (!cur) return;
9585 
9586    len = eina_ustrbuf_length_get(cur->node->unicode);
9587 
9588    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
9589    nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
9590    _evas_textblock_nodes_merge(o, cur->node);
9591    _evas_textblock_cursors_update_offset(cur, nnode, 0, len);
9592    _evas_textblock_cursors_set_node(o, nnode, cur->node);
9593    Efl_Text_Cursor_Handle *co = o->cursor;
9594    if (nnode == co->node)
9595      {
9596         co->node = cur->node;
9597         co->pos += len;
9598      }
9599 }
9600 
9601 /**
9602  * @internal
9603  * Return the format at a specific position.
9604  *
9605  * @param cur the cursor to the position.
9606  * @return the format node at the specific position or NULL if not found.
9607  */
9608 static Evas_Object_Textblock_Node_Format *
_evas_textblock_cursor_node_format_at_pos_get(const Efl_Text_Cursor_Handle * cur)9609 _evas_textblock_cursor_node_format_at_pos_get(const Efl_Text_Cursor_Handle *cur)
9610 {
9611    Evas_Object_Textblock_Node_Format *node;
9612    Evas_Object_Textblock_Node_Format *itr;
9613    int position = 0;
9614 
9615    TB_NULL_CHECK(cur->node, NULL);
9616 
9617    node = cur->node->format_node;
9618    if (!node) return NULL;
9619    /* If there is no exclusive format node to this paragraph return the
9620     * previous's node */
9621    /* Find the main format node */
9622    EINA_INLIST_FOREACH(node, itr)
9623      {
9624         if (itr->text_node != cur->node)
9625           {
9626              return NULL;
9627           }
9628         if ((position + itr->offset) == cur->pos)
9629           {
9630              return itr;
9631           }
9632         position += itr->offset;
9633      }
9634    return NULL;
9635 }
9636 
9637 /**
9638  * @internal
9639  * Return the last format node at the position of the format node n.
9640  *
9641  * @param n a format node at the position.
9642  * @return the last format node at the position of n.
9643  */
9644 static Evas_Object_Textblock_Node_Format *
_evas_textblock_node_format_last_at_off(const Evas_Object_Textblock_Node_Format * n)9645 _evas_textblock_node_format_last_at_off(const Evas_Object_Textblock_Node_Format *n)
9646 {
9647    const Evas_Object_Textblock_Node_Format *nnode;
9648    const Evas_Object_Textblock_Node_Text *tnode;
9649    if (!n) return NULL;
9650    nnode = n;
9651    tnode = n->text_node;
9652    do
9653      {
9654         n = nnode;
9655         nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
9656      }
9657    while (nnode && (nnode->text_node == tnode) && (nnode->offset == 0));
9658 
9659    return (Evas_Object_Textblock_Node_Format *) n;
9660 }
9661 
9662 /**
9663  * @internal
9664  * Returns the visible format at a specific location.
9665  *
9666  * @param n a format at the specific position.
9667  * @return the format node at the specific position or NULL if not found.
9668  */
9669 static Evas_Object_Textblock_Node_Format *
_evas_textblock_node_visible_at_pos_get(const Evas_Object_Textblock_Node_Format * n)9670 _evas_textblock_node_visible_at_pos_get(const Evas_Object_Textblock_Node_Format *n)
9671 {
9672    const Evas_Object_Textblock_Node_Format *nnode;
9673    if (!n) return NULL;
9674    /* The visible format is the last one, because it inserts a replacement
9675     * char that advances the next formats. */
9676 
9677    nnode = n;
9678    do
9679      {
9680         n = nnode;
9681         if (n->visible) return (Evas_Object_Textblock_Node_Format *) n;
9682         nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
9683      }
9684    while (nnode && (nnode->offset == 0));
9685 
9686    return NULL;
9687 }
9688 
9689 /**
9690  * @internal
9691  * Return the last format that applies to a specific cursor or at the specific
9692  * position the cursor points to. This means either a cursor at or before the
9693  * position of the cursor in the text node is returned or the previous's text
9694  * node's format node.
9695  *
9696  * @param cur the position to look at.
9697  * @return the format node found.
9698  */
9699 static Evas_Object_Textblock_Node_Format *
_evas_textblock_cursor_node_format_before_or_at_pos_get(const Efl_Text_Cursor_Handle * cur)9700 _evas_textblock_cursor_node_format_before_or_at_pos_get(const Efl_Text_Cursor_Handle *cur)
9701 {
9702    Evas_Object_Textblock_Node_Format *node, *pitr = NULL;
9703    Evas_Object_Textblock_Node_Format *itr;
9704    size_t position = 0;
9705 
9706    TB_NULL_CHECK(cur->node, NULL);
9707 
9708    node = cur->node->format_node;
9709    if (!node) return NULL;
9710    /* If there is no exclusive format node to this paragraph return the
9711     * previous's node */
9712    if (node->text_node != cur->node)
9713      {
9714         return node;
9715      }
9716    else if (node->offset > cur->pos)
9717      {
9718         return _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
9719      }
9720    /* Find the main format node */
9721    pitr = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
9722    EINA_INLIST_FOREACH(node, itr)
9723      {
9724         if ((itr->text_node != cur->node) ||
9725             ((position + itr->offset) > cur->pos))
9726           {
9727              return pitr;
9728           }
9729         else if ((position + itr->offset) == cur->pos)
9730           {
9731              return itr;
9732           }
9733         pitr = itr;
9734         position += itr->offset;
9735      }
9736    return pitr;
9737 }
9738 
9739 /**
9740  * @internal
9741  * Find the layout item and line that match the cursor.
9742  *
9743  * @param cur the cursor we are currently at. - NOT NULL.
9744  * @param[out] lnr the line found - not null.
9745  * @param[out] itr the item found - not null.
9746  * @return @c EINA_TRUE if we matched the previous format, @c EINA_FALSE
9747  * otherwise.
9748  */
9749 static Eina_Bool
_find_layout_item_match(const Efl_Text_Cursor_Handle * cur,Evas_Object_Textblock_Line ** lnr,Evas_Object_Textblock_Item ** itr)9750 _find_layout_item_match(const Efl_Text_Cursor_Handle *cur, Evas_Object_Textblock_Line **lnr, Evas_Object_Textblock_Item **itr)
9751 {
9752    Efl_Text_Cursor_Handle cur2;
9753    Eina_Bool previous_format = EINA_FALSE;
9754 
9755    _evas_textblock_cursor_init(&cur2, cur->obj);
9756    _evas_textblock_cursor_copy(&cur2, cur);
9757    if (cur2.pos > 0)
9758      {
9759         cur2.pos--;
9760      }
9761 
9762    if (_evas_textblock_cursor_is_at_the_end(cur) &&
9763             _evas_textblock_cursor_format_is_visible_get(&cur2))
9764      {
9765         _find_layout_item_line_match(cur2.obj, cur2.node, cur2.pos, lnr, itr);
9766         previous_format = EINA_TRUE;
9767      }
9768    else
9769      {
9770         _find_layout_item_line_match(cur->obj, cur->node, cur->pos, lnr, itr);
9771      }
9772    return previous_format;
9773 }
9774 
9775 void
_evas_textblock_cursor_init(Efl_Text_Cursor_Handle * cur,const Evas_Object * tb)9776 _evas_textblock_cursor_init(Efl_Text_Cursor_Handle *cur, const Evas_Object *tb)
9777 {
9778    memset(cur, 0, sizeof(Efl_Text_Cursor_Handle));
9779    cur->obj = (Eo *) tb;
9780    cur->cur_objs = NULL;
9781    cur->ref_count = 1;
9782    Efl_Canvas_Textblock_Data *o = efl_data_scope_safe_get(cur->obj, MY_CLASS);
9783    if (!o) return;
9784 
9785    cur->node = o->text_nodes;
9786    cur->pos = 0;
9787 }
9788 
9789 EAPI Efl_Text_Cursor_Handle *
evas_object_textblock_cursor_new(const Evas_Object * eo_obj)9790 evas_object_textblock_cursor_new(const Evas_Object *eo_obj)
9791 {
9792    EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, NULL);
9793    Efl_Text_Cursor_Handle *cur;
9794    Evas_Object_Protected_Data *obj = efl_data_scope_safe_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
9795    if (!obj) return NULL;
9796    evas_object_async_block(obj);
9797 
9798    Efl_Canvas_Textblock_Data *o = efl_data_scope_safe_get(eo_obj, MY_CLASS);
9799    if (!o) return NULL;
9800 
9801    cur = calloc(1, sizeof(Efl_Text_Cursor_Handle));
9802    if (!cur) return NULL;
9803    _evas_textblock_cursor_init(cur, eo_obj);
9804 
9805    o->cursors = eina_list_append(o->cursors, cur);
9806    return cur;
9807 }
9808 
9809 EAPI void
evas_textblock_cursor_free(Evas_Textblock_Cursor * cur)9810 evas_textblock_cursor_free(Evas_Textblock_Cursor *cur)
9811 {
9812    if (!cur) return;
9813    Efl_Canvas_Textblock_Data *o = efl_data_scope_safe_get(cur->obj, MY_CLASS);
9814    if (!o) return;
9815    if (cur == o->cursor) return;
9816    o->cursors = eina_list_remove(o->cursors, cur);
9817    free(cur);
9818 }
9819 
9820 Efl_Text_Cursor_Handle *
evas_textblock_cursor_ref(Efl_Text_Cursor_Handle * cur,Eo * cur_obj)9821 evas_textblock_cursor_ref(Efl_Text_Cursor_Handle *cur, Eo * cur_obj)
9822 {
9823    if (!cur) return NULL;
9824    /* Does not work with TextBlock Main Cursor*/
9825    Efl_Canvas_Textblock_Data *o = efl_data_scope_safe_get(cur->obj, MY_CLASS);
9826    if (!o) return NULL;
9827 
9828    if (cur == o->cursor) return NULL;
9829 
9830    cur->ref_count++;
9831    if (cur_obj) cur->cur_objs = eina_list_append(cur->cur_objs, cur_obj);
9832 
9833    return cur;
9834 }
9835 
9836 void
evas_textblock_cursor_unref(Efl_Text_Cursor_Handle * cursor,Eo * cur_obj)9837 evas_textblock_cursor_unref(Efl_Text_Cursor_Handle *cursor, Eo * cur_obj)
9838 {
9839    if (!cursor) return;
9840    /* Does not work with TextBlock Main Cursor*/
9841    Efl_Canvas_Textblock_Data *o = efl_data_scope_safe_get(cursor->obj, MY_CLASS);
9842    if (!o) return;
9843 
9844    if (cursor == o->cursor) return;
9845 
9846    cursor->ref_count--;
9847    if (cur_obj) cursor->cur_objs = eina_list_remove(cursor->cur_objs, cur_obj);
9848 
9849    if (cursor->ref_count == 0)
9850      {
9851         evas_textblock_cursor_free(cursor);
9852      }
9853 }
9854 
9855 EAPI Eina_Bool
_evas_textblock_cursor_is_format(const Efl_Text_Cursor_Handle * cur)9856 _evas_textblock_cursor_is_format(const Efl_Text_Cursor_Handle *cur)
9857 {
9858    if ((!cur) || (!cur->node)) return EINA_FALSE;
9859    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
9860    evas_object_async_block(obj);
9861    return (_evas_textblock_cursor_node_format_at_pos_get(cur)) ?
9862       EINA_TRUE : EINA_FALSE;
9863 }
9864 
9865 EAPI Eina_Bool
evas_textblock_cursor_is_format(const Evas_Textblock_Cursor * cur)9866 evas_textblock_cursor_is_format(const Evas_Textblock_Cursor *cur)
9867 {
9868    return _evas_textblock_cursor_is_format(cur);
9869 }
9870 
9871 EAPI const Eina_List *
evas_textblock_node_format_list_get(const Eo * eo_obj,const char * anchor)9872 evas_textblock_node_format_list_get(const Eo *eo_obj, const char *anchor)
9873 {
9874    EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, NULL);
9875    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
9876    evas_object_async_block(obj);
9877    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
9878    if (!strcmp(anchor, "a"))
9879      return o->anchors_a;
9880    else if (!strcmp(anchor, "item"))
9881      return o->anchors_item;
9882    return NULL;
9883 }
9884 
9885 EAPI const Evas_Object_Textblock_Node_Format*
evas_textblock_node_format_first_get(Evas_Object * eo_obj)9886 evas_textblock_node_format_first_get(Evas_Object *eo_obj)
9887 {
9888    EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, NULL);
9889    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
9890    evas_object_async_block(obj);
9891    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
9892    return o->format_nodes;
9893 }
9894 
9895 EAPI const Evas_Object_Textblock_Node_Format*
evas_textblock_node_format_last_get(Evas_Object * eo_obj)9896 evas_textblock_node_format_last_get(Evas_Object *eo_obj)
9897 {
9898    EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, NULL);
9899    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
9900    evas_object_async_block(obj);
9901    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
9902    return o->format_nodes ? _NODE_FORMAT(EINA_INLIST_GET(o->format_nodes)->last) : NULL;
9903 }
9904 
9905 EAPI const Evas_Object_Textblock_Node_Format *
evas_textblock_node_format_next_get(const Evas_Object_Textblock_Node_Format * n)9906 evas_textblock_node_format_next_get(const Evas_Object_Textblock_Node_Format *n)
9907 {
9908    if (!n) return NULL;
9909    return _NODE_FORMAT(EINA_INLIST_GET(n)->next);
9910 }
9911 
9912 EAPI const Evas_Object_Textblock_Node_Format *
evas_textblock_node_format_prev_get(const Evas_Object_Textblock_Node_Format * n)9913 evas_textblock_node_format_prev_get(const Evas_Object_Textblock_Node_Format *n)
9914 {
9915    if (!n) return NULL;
9916    return _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
9917 }
9918 
9919 EAPI void
evas_textblock_node_format_remove_pair(Eo * eo_obj,Evas_Object_Textblock_Node_Format * n)9920 evas_textblock_node_format_remove_pair(Eo *eo_obj, Evas_Object_Textblock_Node_Format *n)
9921 {
9922    EINA_SAFETY_ON_NULL_RETURN(eo_obj);
9923    Evas_Object_Textblock_Node_Text *tnode1;
9924    Evas_Object_Textblock_Node_Format *fmt, *found_node = NULL;
9925    Eina_List *fstack = NULL;
9926 
9927    if (!n) return;
9928 
9929    fmt = n;
9930 
9931    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
9932    evas_object_async_block(obj);
9933    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
9934    do
9935      {
9936         const char *fstr = fmt->orig_format;
9937 
9938         if (fmt->opener && !fmt->own_closer)
9939           {
9940              fstack = eina_list_prepend(fstack, fmt);
9941           }
9942         else if (fstr && !fmt->opener)
9943           {
9944              size_t fstr_len;
9945              fstr_len = strlen(fstr);
9946              /* Generic popper, just pop */
9947              if (((fstr[0] == '/') && !fstr[1]) || !fstr[0])
9948                {
9949                   fstack = eina_list_remove_list(fstack, fstack);
9950                   if (!fstack)
9951                     {
9952                        found_node = fmt;
9953                        goto found;
9954                     }
9955                }
9956              /* Find the matching format and pop it, if the matching format
9957               * is out format, i.e the last one, pop and break. */
9958              else
9959                {
9960                   Eina_List *i;
9961                   Evas_Object_Textblock_Node_Format *fnode;
9962                   EINA_LIST_FOREACH(fstack, i, fnode)
9963                     {
9964                        if (_FORMAT_IS_CLOSER_OF(
9965                                 fnode->orig_format, fstr + 1, fstr_len - 1))
9966                          {
9967                             /* Last one, this is our item! */
9968                             if (!eina_list_next(i))
9969                               {
9970                                  found_node = fmt;
9971                                  goto found;
9972                               }
9973                             fstack = eina_list_remove_list(fstack, i);
9974                             break;
9975                          }
9976                     }
9977                }
9978           }
9979 
9980         fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
9981      }
9982    while (fmt && fstack);
9983 
9984 found:
9985 
9986    fstack = eina_list_free(fstack);
9987 
9988    if (n->visible)
9989      {
9990         size_t ind = _evas_textblock_node_format_pos_get(n);
9991         const char *format = n->format;
9992         Efl_Text_Cursor_Handle cur;
9993         _evas_textblock_cursor_init(&cur, eo_obj);
9994 
9995         eina_ustrbuf_remove(n->text_node->unicode, ind, ind + 1);
9996         if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
9997           {
9998              _evas_textblock_cursor_at_format_set(&cur, n);
9999              _evas_textblock_cursor_nodes_merge(&cur);
10000           }
10001         _evas_textblock_cursors_update_offset(&cur, n->text_node, ind, -1);
10002      }
10003    tnode1 = n->text_node;
10004    _evas_textblock_node_format_remove(o, n, 0);
10005    if (found_node && (found_node != n))
10006      {
10007         Evas_Object_Textblock_Node_Text *tnode2;
10008         tnode2 = found_node->text_node;
10009         /* found_node can never be visible! (it's the closing format) */
10010         _evas_textblock_node_format_remove(o, found_node, 0);
10011 
10012         /* FIXME: Should be unified in the layout, for example, added to a list
10013          * that checks this kind of removals. But until then, this is very fast
10014          * and works. */
10015         /* Mark all the text nodes in between the removed formats as dirty. */
10016         while (tnode1)
10017           {
10018              tnode1->dirty = EINA_TRUE;
10019              if (tnode1 == tnode2)
10020                 break;
10021              tnode1 =
10022                 _NODE_TEXT(EINA_INLIST_GET(tnode1)->next);
10023           }
10024      }
10025 
10026    _evas_textblock_changed(o, eo_obj);
10027 }
10028 
10029 EAPI void
evas_textblock_cursor_paragraph_first(Efl_Text_Cursor_Handle * cur)10030 evas_textblock_cursor_paragraph_first(Efl_Text_Cursor_Handle *cur)
10031 {
10032    if (!cur) return;
10033    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
10034    evas_object_async_block(obj);
10035    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
10036    cur->node = o->text_nodes;
10037    cur->pos = 0;
10038    _evas_textblock_cursor_object_changed(cur);
10039 }
10040 
10041 EAPI void
evas_textblock_cursor_paragraph_last(Efl_Text_Cursor_Handle * cur)10042 evas_textblock_cursor_paragraph_last(Efl_Text_Cursor_Handle *cur)
10043 {
10044    Evas_Object_Textblock_Node_Text *node;
10045 
10046    if (!cur) return;
10047    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
10048    evas_object_async_block(obj);
10049    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
10050    node = o->text_nodes;
10051    if (node)
10052      {
10053         node = _NODE_TEXT(EINA_INLIST_GET(node)->last);
10054         cur->node = node;
10055         cur->pos = 0;
10056         evas_textblock_cursor_paragraph_char_last(cur);
10057      }
10058    else
10059      {
10060         cur->node = NULL;
10061         cur->pos = 0;
10062      }
10063    _evas_textblock_cursor_object_changed(cur);
10064 }
10065 
10066 static Eina_Bool
_evas_textblock_cursor_paragraph_next(Efl_Text_Cursor_Handle * cur)10067 _evas_textblock_cursor_paragraph_next(Efl_Text_Cursor_Handle *cur)
10068 {
10069    if (!cur) return EINA_FALSE;
10070    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
10071    evas_object_async_block(obj);
10072    TB_NULL_CHECK(cur->node, EINA_FALSE);
10073    /* If there is a current text node, return the next text node (if exists)
10074     * otherwise, just return False. */
10075    if (cur->node)
10076      {
10077         Evas_Object_Textblock_Node_Text *nnode;
10078         nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
10079         if (nnode)
10080           {
10081              cur->node = nnode;
10082              cur->pos = 0;
10083 
10084              return EINA_TRUE;
10085           }
10086      }
10087    return EINA_FALSE;
10088 }
10089 
10090 EAPI Eina_Bool
evas_textblock_cursor_paragraph_next(Efl_Text_Cursor_Handle * cur)10091 evas_textblock_cursor_paragraph_next(Efl_Text_Cursor_Handle *cur)
10092 {
10093    Eina_Bool b_ret = EINA_FALSE;
10094    if (!cur) return b_ret;
10095    b_ret = _evas_textblock_cursor_paragraph_next(cur);
10096    if (b_ret) _evas_textblock_cursor_object_changed(cur);
10097    return b_ret;
10098 }
10099 
10100 static Eina_Bool
_evas_textblock_cursor_paragraph_prev(Efl_Text_Cursor_Handle * cur)10101 _evas_textblock_cursor_paragraph_prev(Efl_Text_Cursor_Handle *cur)
10102 {
10103    Evas_Object_Textblock_Node_Text *node;
10104    if (!cur) return EINA_FALSE;
10105    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
10106    evas_object_async_block(obj);
10107    TB_NULL_CHECK(cur->node, EINA_FALSE);
10108    /* If the current node is a text node, just get the prev if any,
10109     * if it's a format, get the current text node out of the format and return
10110     * the prev text node if any. */
10111    node = cur->node;
10112    /* If there is a current text node, return the prev text node
10113     * (if exists) otherwise, just return False. */
10114    if (node)
10115      {
10116         Evas_Object_Textblock_Node_Text *pnode;
10117         pnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->prev);
10118         if (pnode)
10119           {
10120              cur->node = pnode;
10121              evas_textblock_cursor_paragraph_char_last(cur);
10122              return EINA_TRUE;
10123           }
10124      }
10125    return EINA_FALSE;
10126 }
10127 
10128 
10129 EAPI Eina_Bool
evas_textblock_cursor_paragraph_prev(Efl_Text_Cursor_Handle * cur)10130 evas_textblock_cursor_paragraph_prev(Efl_Text_Cursor_Handle *cur)
10131 {
10132    Eina_Bool b_ret = EINA_FALSE;
10133    if (!cur) return b_ret;
10134    b_ret = _evas_textblock_cursor_paragraph_prev(cur);
10135    if (b_ret) _evas_textblock_cursor_object_changed(cur);
10136    return b_ret;
10137 }
10138 
10139 EAPI void
evas_textblock_cursor_set_at_format(Evas_Textblock_Cursor * cur,const Evas_Object_Textblock_Node_Format * n)10140 evas_textblock_cursor_set_at_format(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *n)
10141 {
10142    evas_textblock_cursor_at_format_set(cur, n);
10143 }
10144 
10145 EAPI Eina_Bool
evas_textblock_cursor_format_next(Evas_Textblock_Cursor * cur)10146 evas_textblock_cursor_format_next(Evas_Textblock_Cursor *cur)
10147 {
10148    Evas_Object_Textblock_Node_Format *node;
10149 
10150    if (!cur) return EINA_FALSE;
10151    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
10152    evas_object_async_block(obj);
10153    TB_NULL_CHECK(cur->node, EINA_FALSE);
10154    /* If the current node is a format node, just get the next if any,
10155     * if it's a text, get the current format node out of the text and return
10156     * the next format node if any. */
10157    node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
10158    node = _evas_textblock_node_format_last_at_off(node);
10159    if (!node)
10160      {
10161         if (cur->node->format_node)
10162           {
10163              cur->pos = _evas_textblock_node_format_pos_get(node);
10164              return EINA_TRUE;
10165           }
10166      }
10167    /* If there is a current text node, return the next format node (if exists)
10168     * otherwise, just return False. */
10169    else
10170      {
10171         Evas_Object_Textblock_Node_Format *nnode;
10172         nnode = _NODE_FORMAT(EINA_INLIST_GET(node)->next);
10173         if (nnode)
10174           {
10175              cur->node = nnode->text_node;
10176              cur->pos = _evas_textblock_node_format_pos_get(nnode);
10177 
10178              return EINA_TRUE;
10179           }
10180      }
10181    return EINA_FALSE;
10182 }
10183 
10184 EAPI Eina_Bool
evas_textblock_cursor_format_prev(Evas_Textblock_Cursor * cur)10185 evas_textblock_cursor_format_prev(Evas_Textblock_Cursor *cur)
10186 {
10187    const Evas_Object_Textblock_Node_Format *node;
10188    if (!cur) return EINA_FALSE;
10189    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
10190    evas_object_async_block(obj);
10191    TB_NULL_CHECK(cur->node, EINA_FALSE);
10192    node = evas_textblock_cursor_format_get(cur);
10193    if (!node)
10194      {
10195         node = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
10196         if (node)
10197           {
10198              cur->node = node->text_node;
10199              cur->pos = _evas_textblock_node_format_pos_get(node);
10200 
10201              return EINA_TRUE;
10202           }
10203      }
10204    /* If there is a current text node, return the next text node (if exists)
10205     * otherwise, just return False. */
10206    if (node)
10207      {
10208         Evas_Object_Textblock_Node_Format *pnode;
10209         pnode = _NODE_FORMAT(EINA_INLIST_GET(node)->prev);
10210         if (pnode)
10211           {
10212              cur->node = pnode->text_node;
10213              cur->pos = _evas_textblock_node_format_pos_get(pnode);
10214 
10215              return EINA_TRUE;
10216           }
10217      }
10218    return EINA_FALSE;
10219 }
10220 
10221 /* BREAK_AFTER: true if we can break after the current char.
10222  * Both macros assume str[i] is not the terminating nul */
10223 #define BREAK_AFTER(i) \
10224    (breaks[i] == WORDBREAK_BREAK)
10225 
10226 EAPI Eina_Bool
evas_textblock_cursor_word_start(Efl_Text_Cursor_Handle * cur)10227 evas_textblock_cursor_word_start(Efl_Text_Cursor_Handle *cur)
10228 {
10229    if (!cur) return EINA_FALSE;
10230    const Eina_Unicode *text;
10231    size_t i;
10232    char *breaks;
10233    size_t old_cursor_pos = cur->pos;
10234 
10235    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
10236    evas_object_async_block(obj);
10237    TB_NULL_CHECK(cur->node, EINA_FALSE);
10238 
10239    size_t len = eina_ustrbuf_length_get(cur->node->unicode);
10240 
10241    text = eina_ustrbuf_string_get(cur->node->unicode);
10242 
10243      {
10244         const char *lang = ""; /* FIXME: get lang */
10245         breaks = malloc(len);
10246         set_wordbreaks_utf32((const utf32_t *) text, len, lang, breaks);
10247      }
10248 
10249    if ((cur->pos > 0) && (cur->pos == len))
10250       cur->pos--;
10251 
10252    for (i = cur->pos ; _is_white(text[i]) && BREAK_AFTER(i) ; i--)
10253      {
10254         if (i == 0)
10255           {
10256              Evas_Object_Textblock_Node_Text *pnode;
10257              pnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->prev);
10258              if (pnode)
10259                {
10260                   cur->node = pnode;
10261                   len = eina_ustrbuf_length_get(cur->node->unicode);
10262                   cur->pos = len - 1;
10263                   free(breaks);
10264                   return evas_textblock_cursor_word_start(cur);
10265                }
10266              else
10267                {
10268                   break;
10269                }
10270           }
10271      }
10272 
10273    for ( ; i > 0 ; i--)
10274      {
10275         if (BREAK_AFTER(i - 1))
10276           {
10277              break;
10278           }
10279      }
10280 
10281    cur->pos = i;
10282 
10283    free(breaks);
10284    if (cur->pos != old_cursor_pos)
10285      {
10286         _evas_textblock_cursor_object_changed(cur);
10287         return EINA_TRUE;
10288      }
10289    return EINA_FALSE;
10290 }
10291 
10292 EAPI Eina_Bool
evas_textblock_cursor_word_end(Efl_Text_Cursor_Handle * cur)10293 evas_textblock_cursor_word_end(Efl_Text_Cursor_Handle *cur)
10294 {
10295    if (!cur) return EINA_FALSE;
10296    const Eina_Unicode *text;
10297    size_t i;
10298    char *breaks;
10299    size_t old_cursor_pos = cur->pos;
10300 
10301    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
10302    evas_object_async_block(obj);
10303    TB_NULL_CHECK(cur->node, EINA_FALSE);
10304 
10305    size_t len = eina_ustrbuf_length_get(cur->node->unicode);
10306 
10307    // No movement happend, return false
10308    if (cur->pos == len)
10309      return EINA_FALSE;
10310 
10311    text = eina_ustrbuf_string_get(cur->node->unicode);
10312 
10313      {
10314         const char *lang = ""; /* FIXME: get lang */
10315         breaks = malloc(len);
10316         set_wordbreaks_utf32((const utf32_t *) text, len, lang, breaks);
10317      }
10318 
10319    for (i = cur->pos; text[i] && _is_white(text[i]) && (BREAK_AFTER(i)) ; i++);
10320    if (i == len)
10321      {
10322         Evas_Object_Textblock_Node_Text *nnode;
10323         nnode = _NODE_TEXT(EINA_INLIST_GET(cur->node)->next);
10324         if (nnode)
10325           {
10326              cur->node = nnode;
10327              cur->pos = 0;
10328              free(breaks);
10329              return evas_textblock_cursor_word_end(cur);
10330           }
10331      }
10332 
10333    for ( ; text[i] ; i++)
10334      {
10335         if (BREAK_AFTER(i))
10336           {
10337              /* This is the one to break after. */
10338              break;
10339           }
10340      }
10341 
10342    cur->pos = i;
10343 
10344    free(breaks);
10345    if (cur->pos != old_cursor_pos)
10346      {
10347         _evas_textblock_cursor_object_changed(cur);
10348         return EINA_TRUE;
10349      }
10350    return EINA_FALSE;
10351 }
10352 
10353 static char *
_evas_textblock_grapheme_breaks_new(Evas_Object_Textblock_Item * it,size_t len)10354 _evas_textblock_grapheme_breaks_new(Evas_Object_Textblock_Item *it, size_t len)
10355 {
10356    char *grapheme_breaks = NULL;
10357    const char *lang = (it->format->font.fdesc) ? it->format->font.fdesc->lang : "";
10358 
10359    grapheme_breaks = malloc(len);
10360    if (!grapheme_breaks) return NULL;
10361 
10362    set_graphemebreaks_utf32((const utf32_t *)
10363                             eina_ustrbuf_string_get(
10364                                it->text_node->unicode),
10365                             len, lang, grapheme_breaks);
10366 
10367    return grapheme_breaks;
10368 }
10369 
10370 static size_t
_evas_textblock_cursor_cluster_pos_get(Evas_Textblock_Cursor * cur,Eina_Bool inc,Eina_Bool * is_single_glyph)10371 _evas_textblock_cursor_cluster_pos_get(Evas_Textblock_Cursor *cur, Eina_Bool inc, Eina_Bool *is_single_glyph)
10372 {
10373    Evas_Object_Textblock_Paragraph *par;
10374    Efl_Canvas_Textblock_Data *o;
10375    size_t cur_pos = cur->pos;
10376    size_t ret = cur->pos;
10377 
10378    if (!inc) cur_pos--;
10379 
10380    if (!cur->node->par)
10381      {
10382         o = efl_data_scope_get(cur->obj, MY_CLASS);
10383         if (o) _relayout_if_needed(cur->obj, o);
10384      }
10385 
10386    par = cur->node->par;
10387 
10388    if (par)
10389      {
10390         Eina_List *l;
10391         Evas_Object_Textblock_Item *it, *last_it = NULL;
10392         EINA_LIST_FOREACH(par->logical_items, l, it)
10393           {
10394              if (it->text_pos > cur_pos)
10395                {
10396                   if (!last_it) last_it = it;
10397                   break;
10398                }
10399              last_it = it;
10400           }
10401 
10402         if (last_it)
10403           {
10404              it = last_it;
10405              if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
10406                {
10407                   size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
10408                   char *grapheme_breaks = _evas_textblock_grapheme_breaks_new(it, len);
10409 
10410                   if (grapheme_breaks)
10411                     {
10412                        size_t grapheme_breaks_index = cur_pos;
10413 
10414                        if (inc)
10415                          {
10416                             while ((grapheme_breaks_index < len) &&
10417                                    (grapheme_breaks[grapheme_breaks_index] != GRAPHEMEBREAK_BREAK))
10418                               {
10419                                  grapheme_breaks_index++;
10420                               }
10421 
10422                             ret = grapheme_breaks_index + 1;
10423                          }
10424                        else
10425                          {
10426                             while ((grapheme_breaks_index > 0) &&
10427                                    (grapheme_breaks[grapheme_breaks_index - 1] != GRAPHEMEBREAK_BREAK))
10428                               {
10429                                  grapheme_breaks_index--;
10430                               }
10431 
10432                             ret = grapheme_breaks_index;
10433                          }
10434 
10435                        free(grapheme_breaks);
10436                     }
10437 #ifdef OT_SUPPORT
10438                   if (is_single_glyph)
10439                     {
10440                        Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(last_it);
10441                        Evas_Text_Props_Info *info = ti->text_props.info;
10442                        int it_index = ((inc) ? cur->pos : ret) - last_it->text_pos;
10443                        *is_single_glyph = EINA_FALSE;
10444                        Evas_Font_OT_Info ot = {0};
10445                        Evas_BiDi_Direction itdir = ti->text_props.bidi_dir;
10446                        if (ti->text_props.len != ti->text_props.text_len)/*if code point count same as glyph count skip it*/
10447                          {
10448                             int i = 0;
10449                             if (itdir == EFL_TEXT_BIDIRECTIONAL_TYPE_RTL)
10450                               {
10451                                  for (i = ti->text_props.len-1 ; i >= 0; i--)
10452                                    {
10453                                       ot = info->ot[i];
10454                                       if (ot.source_cluster >= (size_t)it_index)
10455                                          break;
10456                                    }
10457                                  if (i <= 0)
10458                                    {
10459                                       if (ti->text_props.text_len - ot.source_cluster >= 2)
10460                                          *is_single_glyph = EINA_TRUE;
10461                                    }
10462                                  else
10463                                    {
10464                                       Evas_Font_OT_Info ot_next = info->ot[i - 1];
10465                                       if (ot_next.source_cluster - ot.source_cluster >= 2)
10466                                          *is_single_glyph = EINA_TRUE;
10467                                    }
10468                               }
10469                             else
10470                               {
10471                                  for (i = 0; i < (int) ti->text_props.len; i++)
10472                                    {
10473                                       ot = info->ot[i];
10474                                       if ((int)ot.source_cluster >= it_index)
10475                                          break;
10476                                    }
10477                                  if ((i + 1) >= (int) ti->text_props.len)
10478                                    {
10479                                       if (ti->text_props.text_len - ot.source_cluster >= 2)
10480                                          *is_single_glyph = EINA_TRUE;
10481                                    }
10482                                  else
10483                                    {
10484                                       Evas_Font_OT_Info ot_next = info->ot[i + 1];
10485                                       if (ot_next.source_cluster - ot.source_cluster >= 2)
10486                                          *is_single_glyph = EINA_TRUE;
10487                                    }
10488                               }
10489                          }
10490                        if (*is_single_glyph == EINA_FALSE)
10491                          {
10492                            Eina_Unicode content = 0;
10493                            if (!inc && cur->pos > 0)
10494                               content = eina_ustrbuf_string_get(cur->node->unicode)[cur->pos - 1];
10495                            else if (inc && eina_ustrbuf_length_get(cur->node->unicode) > (cur->pos + 1))
10496                               content = eina_ustrbuf_string_get(cur->node->unicode)[cur->pos + 1];
10497                            if (VAR_SEQ(content)) *is_single_glyph = EINA_TRUE;
10498                          }
10499                     }
10500 #else//#ifdef OT_SUPPORT
10501                   (void)is_single_glyph;
10502 #endif//#ifdef OT_SUPPORT
10503                }
10504           }
10505      }
10506 
10507    return ret;
10508 }
10509 
evas_textblock_cursor_at_cluster_as_single_glyph(Evas_Textblock_Cursor * cur,Eina_Bool forward)10510 EAPI Eina_Bool evas_textblock_cursor_at_cluster_as_single_glyph(Evas_Textblock_Cursor *cur,Eina_Bool forward)
10511 {
10512    Eina_Bool is_single_glyph = EINA_FALSE;
10513    size_t ret = _evas_textblock_cursor_cluster_pos_get(cur, forward, &is_single_glyph);
10514    size_t distance = (ret > cur->pos) ? ret - cur->pos : cur->pos - ret;
10515     if ((distance > 1) && is_single_glyph)
10516       {
10517          return EINA_TRUE;
10518       }
10519    return EINA_FALSE;
10520 }
10521 
10522 static Eina_Bool
_evas_textblock_cursor_next(Evas_Textblock_Cursor * cur,Eina_Bool per_cluster)10523 _evas_textblock_cursor_next(Evas_Textblock_Cursor *cur, Eina_Bool per_cluster)
10524 {
10525    Evas_Object_Protected_Data *obj;
10526    const Eina_Unicode *text;
10527    int ind;
10528 
10529    if (!cur) return EINA_FALSE;
10530    TB_NULL_CHECK(cur->node, EINA_FALSE);
10531 
10532    obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
10533    evas_object_async_block(obj);
10534 
10535    ind = cur->pos;
10536    text = eina_ustrbuf_string_get(cur->node->unicode);
10537 
10538    if (text[ind])
10539      {
10540         if (per_cluster)
10541           ind = _evas_textblock_cursor_cluster_pos_get(cur, EINA_TRUE, NULL);
10542 
10543         if (ind <= (int)cur->pos)
10544           ind = cur->pos + 1;
10545      }
10546 
10547    /* Only allow pointing a null if it's the last paragraph.
10548     * because we don't have a PS there. */
10549    if (text[ind])
10550      {
10551         cur->pos = ind;
10552         return EINA_TRUE;
10553      }
10554    else
10555      {
10556         if (!_evas_textblock_cursor_paragraph_next(cur))
10557           {
10558              /* If we already were at the end, that means we don't have
10559               * where to go next we should return FALSE */
10560              if (cur->pos == (size_t) ind)
10561                return EINA_FALSE;
10562 
10563              cur->pos = ind;
10564              return EINA_TRUE;
10565           }
10566         else
10567           {
10568              return EINA_TRUE;
10569           }
10570      }
10571 }
10572 
10573 static Eina_Bool
_evas_textblock_cursor_prev(Evas_Textblock_Cursor * cur,Eina_Bool per_cluster)10574 _evas_textblock_cursor_prev(Evas_Textblock_Cursor *cur, Eina_Bool per_cluster)
10575 {
10576    Evas_Object_Protected_Data *obj;
10577 
10578    if (!cur) return EINA_FALSE;
10579    TB_NULL_CHECK(cur->node, EINA_FALSE);
10580 
10581    obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
10582    evas_object_async_block(obj);
10583 
10584    if (cur->pos != 0)
10585      {
10586         if (per_cluster)
10587           {
10588              size_t ret = _evas_textblock_cursor_cluster_pos_get(cur, EINA_FALSE, NULL);
10589 
10590              if (ret != cur->pos)
10591                {
10592                   cur->pos = ret;
10593                   return EINA_TRUE;
10594                }
10595           }
10596 
10597         cur->pos--;
10598         return EINA_TRUE;
10599      }
10600    return evas_textblock_cursor_paragraph_prev(cur);
10601 }
10602 
10603 EAPI Eina_Bool
evas_textblock_cursor_char_next(Efl_Text_Cursor_Handle * cur)10604 evas_textblock_cursor_char_next(Efl_Text_Cursor_Handle *cur)
10605 {
10606    Eina_Bool b_ret = _evas_textblock_cursor_next(cur, EINA_FALSE);
10607    if (b_ret) _evas_textblock_cursor_object_changed(cur);
10608    return b_ret;
10609 }
10610 
10611 EAPI Eina_Bool
evas_textblock_cursor_char_prev(Efl_Text_Cursor_Handle * cur)10612 evas_textblock_cursor_char_prev(Efl_Text_Cursor_Handle *cur)
10613 {
10614    Eina_Bool b_ret = _evas_textblock_cursor_prev(cur, EINA_FALSE);
10615    if (b_ret) _evas_textblock_cursor_object_changed(cur);
10616    return b_ret;
10617 }
10618 
10619 EAPI Eina_Bool
evas_textblock_cursor_cluster_next(Efl_Text_Cursor_Handle * cur)10620 evas_textblock_cursor_cluster_next(Efl_Text_Cursor_Handle *cur)
10621 {
10622    Eina_Bool b_ret = _evas_textblock_cursor_next(cur, EINA_TRUE);
10623    if (b_ret) _evas_textblock_cursor_object_changed(cur);
10624    return b_ret;
10625 }
10626 
10627 EAPI Eina_Bool
evas_textblock_cursor_cluster_prev(Efl_Text_Cursor_Handle * cur)10628 evas_textblock_cursor_cluster_prev(Efl_Text_Cursor_Handle *cur)
10629 {
10630    Eina_Bool b_ret = _evas_textblock_cursor_prev(cur, EINA_TRUE);
10631    if (b_ret) _evas_textblock_cursor_object_changed(cur);
10632    return b_ret;
10633 }
10634 
10635 EAPI void
evas_textblock_cursor_paragraph_char_first(Efl_Text_Cursor_Handle * cur)10636 evas_textblock_cursor_paragraph_char_first(Efl_Text_Cursor_Handle *cur)
10637 {
10638    if (!cur) return;
10639    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
10640    evas_object_async_block(obj);
10641    cur->pos = 0;
10642    _evas_textblock_cursor_object_changed(cur);
10643 }
10644 
10645 EAPI void
evas_textblock_cursor_paragraph_char_last(Efl_Text_Cursor_Handle * cur)10646 evas_textblock_cursor_paragraph_char_last(Efl_Text_Cursor_Handle *cur)
10647 {
10648    int ind;
10649 
10650    if (!cur) return;
10651    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
10652    evas_object_async_block(obj);
10653    TB_NULL_CHECK(cur->node);
10654    ind = eina_ustrbuf_length_get(cur->node->unicode);
10655    /* If it's not the last paragraph, go back one, because we want to point
10656     * to the PS, not the NULL */
10657    if (EINA_INLIST_GET(cur->node)->next)
10658       ind--;
10659 
10660    if (ind >= 0)
10661       cur->pos = ind;
10662    else
10663       cur->pos = 0;
10664 
10665    _evas_textblock_cursor_object_changed(cur);
10666 }
10667 
10668 static void
_cursor_line_first_char_get(Evas_Object_Textblock_Line * ln,Efl_Text_Cursor_Handle * cur,Efl_Canvas_Textblock_Data * o)10669 _cursor_line_first_char_get(Evas_Object_Textblock_Line *ln,
10670                             Efl_Text_Cursor_Handle *cur,
10671                             Efl_Canvas_Textblock_Data *o)
10672 {
10673    if (ln->items)
10674      {
10675         Evas_Object_Textblock_Item *it;
10676         size_t pos;
10677         pos = ln->items->text_pos;
10678         EINA_INLIST_FOREACH(EINA_INLIST_GET(ln->items)->next, it)
10679           {
10680              if (it->text_pos < pos)
10681                {
10682                   pos = it->text_pos;
10683                }
10684           }
10685         cur->pos = pos;
10686         cur->node = ln->items->text_node;
10687      }
10688    else
10689      {
10690         cur->pos = 0;
10691         cur->node = o->text_nodes;
10692      }
10693 }
10694 
10695 EAPI void
evas_textblock_cursor_line_char_first(Efl_Text_Cursor_Handle * cur)10696 evas_textblock_cursor_line_char_first(Efl_Text_Cursor_Handle *cur)
10697 {
10698    Evas_Object_Textblock_Line *ln = NULL;
10699    Evas_Object_Textblock_Item *it = NULL;
10700    if (!cur) return;
10701    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
10702    evas_object_async_block(obj);
10703 
10704    evas_object_async_block(obj);
10705    TB_NULL_CHECK(cur->node);
10706    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
10707 
10708    _relayout_if_needed(cur->obj, o);
10709 
10710    /* We don't actually need 'it', but it needs to be non NULL */
10711    _find_layout_item_match(cur, &ln, &it);
10712 
10713    if (!ln) return;
10714 
10715    _cursor_line_first_char_get(ln, cur, o);
10716 
10717    _evas_textblock_cursor_object_changed(cur);
10718 }
10719 
10720 EAPI void
evas_textblock_cursor_line_char_last(Efl_Text_Cursor_Handle * cur)10721 evas_textblock_cursor_line_char_last(Efl_Text_Cursor_Handle *cur)
10722 {
10723    Evas_Object_Textblock_Line *ln = NULL;
10724    Evas_Object_Textblock_Item *it = NULL;
10725 
10726    if (!cur) return;
10727    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
10728    evas_object_async_block(obj);
10729    TB_NULL_CHECK(cur->node);
10730    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
10731 
10732    _relayout_if_needed(cur->obj, o);
10733 
10734    _find_layout_item_match(cur, &ln, &it);
10735 
10736    if (!ln) return;
10737    if (ln->items)
10738      {
10739         Evas_Object_Textblock_Item *i;
10740         it = ln->items;
10741         EINA_INLIST_FOREACH(ln->items, i)
10742           {
10743              if (it->text_pos < i->text_pos)
10744                {
10745                   it = i;
10746                }
10747           }
10748      }
10749    if (it)
10750      {
10751         size_t ind;
10752 
10753         cur->node = it->text_node;
10754         cur->pos = it->text_pos;
10755         if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
10756           {
10757              ind = _ITEM_TEXT(it)->text_props.text_len - 1;
10758              if (!IS_AT_END(_ITEM_TEXT(it), ind)) ind++;
10759              cur->pos += ind;
10760           }
10761         else if (!EINA_INLIST_GET(ln)->next && !EINA_INLIST_GET(ln->par)->next)
10762           {
10763              cur->pos++;
10764           }
10765      }
10766 
10767    _evas_textblock_cursor_object_changed(cur);
10768 }
10769 /**
10770  * @internal
10771  * checks if a format (as a string) is visible/changes format and sets the
10772  * fnode properties accordingly.
10773  *
10774  * @param fnode the format node
10775  * @param s the string.
10776  */
10777 static void
_evas_textblock_format_is_visible(Evas_Object_Textblock_Node_Format * fnode,const char * s,Eina_Bool is_item)10778 _evas_textblock_format_is_visible(Evas_Object_Textblock_Node_Format *fnode,
10779       const char *s, Eina_Bool is_item)
10780 {
10781    const char *item;
10782    Eina_Bool is_opener = EINA_TRUE;
10783 
10784    fnode->visible = fnode->format_change = EINA_FALSE;
10785    fnode->anchor = ANCHOR_NONE;
10786    if (!s) return;
10787 
10788    if (!fnode->own_closer)
10789      {
10790         is_opener = fnode->opener;
10791         fnode->format_change = EINA_TRUE;
10792      }
10793 
10794    while ((item = _format_parse(&s, EINA_FALSE)))
10795      {
10796         int itlen = s - item;
10797         /* We care about all of the formats even after a - except for
10798          * item which we don't care after a - because it's just a standard
10799          * closing */
10800         if ((!strncmp(item, "\n", itlen) || !strncmp(item, "\\n", itlen)) ||
10801               (!strncmp(item, "\t", itlen) || !strncmp(item, "\\t", itlen)) ||
10802               (!strncmp(item, "br", itlen) && (itlen >= 2)) ||
10803               (!strncmp(item, "tab", itlen) && (itlen >= 3)) ||
10804               (!strncmp(item, "ps", itlen) && (itlen >= 2)) ||
10805               (is_opener &&
10806                (is_item ||
10807                 (fnode->annotation && fnode->annotation->is_item) ||
10808                 ((!strncmp(item, "item", itlen) && (itlen >= 4))))))
10809           {
10810              fnode->visible = EINA_TRUE;
10811           }
10812 
10813         if (is_opener && !strncmp(item, "a", itlen))
10814           {
10815              fnode->anchor = ANCHOR_A;
10816           }
10817         else if (is_opener &&
10818               ((fnode->annotation && fnode->annotation->is_item) ||
10819                  (!strncmp(item, "item", itlen) && (itlen >= 4))))
10820           {
10821              fnode->anchor = ANCHOR_ITEM;
10822           }
10823      }
10824 }
10825 
10826 /**
10827  * Sets the cursor to the position of where the fmt points to.
10828  *
10829  * @param cur the cursor to update.
10830  * @param fmt the format to set according to.
10831  * @return nothing.
10832  */
10833 static void EINA_UNUSED
_evas_textblock_cursor_node_text_at_format(Efl_Text_Cursor_Handle * cur,Evas_Object_Textblock_Node_Format * fmt)10834 _evas_textblock_cursor_node_text_at_format(Efl_Text_Cursor_Handle *cur, Evas_Object_Textblock_Node_Format *fmt)
10835 {
10836    Evas_Object_Textblock_Node_Text *text;
10837    Evas_Object_Textblock_Node_Format *base_format;
10838    Evas_Object_Textblock_Node_Format *itr;
10839    size_t position = 0;
10840 
10841    if (!cur || !fmt) return;
10842    /* Find the main format node */
10843    text = fmt->text_node;
10844    cur->node = text;
10845    base_format = text->format_node;
10846    EINA_INLIST_FOREACH(base_format, itr)
10847      {
10848         if (itr == fmt)
10849           {
10850              break;
10851           }
10852         position += itr->offset;
10853      }
10854    cur->pos = position;
10855 
10856 }
10857 
10858 
10859 /**
10860  * @internal
10861  * Remove pairs of + and - formats and also remove formats without + or -
10862  * i.e formats that pair to themselves. Only removes invisible formats
10863  * that pair themselves, if you want to remove invisible formats that pair
10864  * themselves, please first change fmt->visible to EINA_FALSE.
10865  *
10866  * @param o the textblock object.
10867  * @param fmt the current format.
10868  */
10869 static void
_evas_textblock_node_format_remove_matching(Efl_Canvas_Textblock_Data * o,Evas_Object_Textblock_Node_Format * fmt)10870 _evas_textblock_node_format_remove_matching(Efl_Canvas_Textblock_Data *o,
10871       Evas_Object_Textblock_Node_Format *fmt)
10872 {
10873    Evas_Object_Textblock_Node_Text *tnode;
10874    Eina_List *formats = NULL;
10875    size_t offset = 0;
10876 
10877    if (!fmt) return;
10878 
10879    tnode = fmt->text_node;
10880 
10881    do
10882      {
10883         Evas_Object_Textblock_Node_Format *nnode;
10884         const char *fstr = fmt->orig_format;
10885 
10886         nnode = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
10887         if (nnode)
10888           {
10889              offset = nnode->offset;
10890           }
10891 
10892 
10893         if (fmt->opener && !fmt->own_closer)
10894           {
10895              formats = eina_list_prepend(formats, fmt);
10896           }
10897         else if (fstr && !fmt->opener)
10898           {
10899              Evas_Object_Textblock_Node_Format *fnode;
10900              size_t fstr_len;
10901              fstr_len = strlen(fstr);
10902              /* Generic popper, just pop (if there's anything to pop). */
10903              if (formats && (((fstr[0] == '/') && !fstr[1]) || !fstr[0]))
10904                {
10905                   fnode = eina_list_data_get(formats);
10906                   formats = eina_list_remove_list(formats, formats);
10907                   _evas_textblock_node_format_remove(o, fnode, 0);
10908                   _evas_textblock_node_format_remove(o, fmt, 0);
10909                }
10910              /* Find the matching format and pop it, if the matching format
10911               * is our format, i.e the last one, pop and break. */
10912              else
10913                {
10914                   Eina_List *i, *next;
10915                   EINA_LIST_FOREACH_SAFE(formats, i, next, fnode)
10916                     {
10917                        if (_FORMAT_IS_CLOSER_OF(
10918                                 fnode->orig_format, fstr + 1, fstr_len - 1))
10919                          {
10920                             Efl_Text_Attribute_Handle *an = fmt->annotation;
10921 
10922                             fnode = eina_list_data_get(i);
10923                             formats = eina_list_remove_list(formats, i);
10924                             _evas_textblock_node_format_remove(o, fnode, 0);
10925                             _evas_textblock_node_format_remove(o, fmt, 0);
10926 
10927                             if (an)
10928                               {
10929                                  _evas_textblock_annotation_remove(
10930                                        NULL, o, an, EINA_FALSE, EINA_FALSE);
10931                               }
10932                             break;
10933                          }
10934                     }
10935                }
10936           }
10937         else if (!fmt->visible)
10938           {
10939              _evas_textblock_node_format_remove(o, fmt, 0);
10940           }
10941         fmt = nnode;
10942      }
10943    while (fmt && (offset == 0) && (fmt->text_node == tnode));
10944    eina_list_free(formats);
10945 }
10946 
10947 /**
10948  * @internal
10949  * Add the offset (may be negative) to the first node after fmt which is
10950  * pointing to the text node tnode or to o->format_nodes if fmt is null
10951  * and it points to tnode.
10952  *
10953  * @param o the textblock object.
10954  * @param tnode the text node the format should point to.
10955  * @param fmt the current format.
10956  * @param offset the offset to add (may be negative).
10957  */
10958 static void
_evas_textblock_node_format_adjust_offset(Efl_Canvas_Textblock_Data * o,Evas_Object_Textblock_Node_Text * tnode,Evas_Object_Textblock_Node_Format * fmt,int offset)10959 _evas_textblock_node_format_adjust_offset(Efl_Canvas_Textblock_Data *o,
10960       Evas_Object_Textblock_Node_Text *tnode,
10961       Evas_Object_Textblock_Node_Format *fmt, int offset)
10962 {
10963    if (fmt)
10964      {
10965         fmt = _NODE_FORMAT(EINA_INLIST_GET(fmt)->next);
10966      }
10967    else
10968      {
10969         fmt = o->format_nodes;
10970      }
10971    if (fmt && (tnode == fmt->text_node))
10972      {
10973         fmt->offset += offset;
10974      }
10975 }
10976 
10977 /**
10978  * @internal
10979  * Removes a format node updating the offset of the next format node and the
10980  * text nodes pointing to this node.
10981  *
10982  * @param o the textblock object.
10983  * @param n the fromat node to remove
10984  */
10985 static void
_evas_textblock_node_format_remove(Efl_Canvas_Textblock_Data * o,Evas_Object_Textblock_Node_Format * n,int visible_adjustment)10986 _evas_textblock_node_format_remove(Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Node_Format *n, int visible_adjustment)
10987 {
10988    /* Update the text nodes about the change */
10989      {
10990         Evas_Object_Textblock_Node_Format *nnode;
10991         nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->next);
10992         /* If there's a next node that belongs to the same text node
10993          * and the curret node was the main one, advance the format node */
10994         if (nnode && (nnode->text_node == n->text_node))
10995           {
10996              if (nnode->text_node->format_node == n)
10997                {
10998                   nnode->text_node->format_node = nnode;
10999                }
11000           }
11001         else
11002           {
11003              Evas_Object_Textblock_Node_Text *tnode;
11004              /* If there's no next one update the text nodes */
11005              nnode = _NODE_FORMAT(EINA_INLIST_GET(n)->prev);
11006              tnode = n->text_node;
11007              /* Even if it's not the current text_node's main node
11008               * it can still be the next's. */
11009              if (tnode && (tnode->format_node != n))
11010                {
11011                   tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
11012                }
11013              while (tnode && (tnode->format_node == n))
11014                {
11015                   tnode->format_node = nnode;
11016                   tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next);
11017                }
11018           }
11019      }
11020    _evas_textblock_node_format_adjust_offset(o, n->text_node, n,
11021          n->offset - visible_adjustment);
11022 
11023    o->format_nodes = _NODE_FORMAT(eina_inlist_remove(
11024            EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n)));
11025    _evas_textblock_node_format_free(o, n);
11026 }
11027 
11028 void
_evas_textblock_annotations_node_format_remove(Evas_Object * eo_obj,Evas_Object_Textblock_Node_Format * n,int visual_adjustment)11029 _evas_textblock_annotations_node_format_remove(Evas_Object *eo_obj, Evas_Object_Textblock_Node_Format *n, int visual_adjustment)
11030 {
11031    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
11032    _evas_textblock_node_format_remove(o, n, visual_adjustment);
11033 }
11034 
11035 /**
11036  * @internal
11037  * Sets all the offsets of the format nodes between start and end in the text
11038  * node n to 0 and sets visibility to EINA_FALSE.
11039  * If end == -1 end means the end of the string.
11040  * Assumes there is a prev node or the current node will be preserved.
11041  *
11042  * @param n the text node the positions refer to.
11043  * @param start the start of where to delete from.
11044  * @param end the end of the section to delete, if end == -1 it means the end of the string.
11045  * @returns @c EINA_TRUE if removed a PS, @c EINA_FALSE otherwise.
11046  */
11047 static Eina_Bool
_evas_textblock_node_text_adjust_offsets_to_start(Efl_Canvas_Textblock_Data * o,Evas_Object_Textblock_Node_Text * n,size_t start,int end)11048 _evas_textblock_node_text_adjust_offsets_to_start(Efl_Canvas_Textblock_Data *o,
11049       Evas_Object_Textblock_Node_Text *n, size_t start, int end)
11050 {
11051    Evas_Object_Textblock_Node_Format *last_node, *itr;
11052    int use_end = 1;
11053    int delta = 0;
11054    int first = 1;
11055    size_t pos = 0;
11056    int orig_end;
11057 
11058    if ((start == 0) && (end == 0))
11059      return EINA_FALSE;
11060 
11061    itr = n->format_node;
11062    if (!itr || (itr->text_node != n)) return EINA_FALSE;
11063 
11064    orig_end = end;
11065    if ((end < 0) || ((size_t) end == eina_ustrbuf_length_get(n->unicode)))
11066      {
11067         use_end = 0;
11068      }
11069    else if (end > 0)
11070      {
11071         /* We don't want the last one */
11072         end--;
11073      }
11074 
11075    /* Find the first node after start */
11076    while (itr && (itr->text_node == n))
11077      {
11078         pos += itr->offset;
11079         if (pos >= start)
11080           {
11081              break;
11082           }
11083         itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
11084      }
11085 
11086    if (!itr || (itr->text_node != n))
11087      {
11088         return EINA_FALSE;
11089      }
11090 
11091    delta = orig_end - pos;
11092    itr->offset -= pos - start;
11093 
11094    while (itr && (itr->text_node == n))
11095      {
11096         last_node = itr;
11097         itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
11098 
11099         if (!first)
11100           {
11101              pos += last_node->offset;
11102           }
11103 
11104         /* start is negative when this gets relevant */
11105         if (use_end && (pos > (size_t) end))
11106           {
11107              last_node->offset -= delta;
11108              break;
11109           }
11110 
11111         delta = orig_end - pos;
11112         if (!first)
11113           {
11114              last_node->offset = 0;
11115           }
11116         else
11117           {
11118              first = 0;
11119           }
11120         last_node->visible = EINA_FALSE;
11121 
11122         if (!itr || (itr->text_node != n))
11123           {
11124              /* Remove the PS, and return since it's the end of the node */
11125              if (_IS_PARAGRAPH_SEPARATOR(o, last_node->format))
11126                {
11127                   _evas_textblock_node_format_remove(o, last_node, 0);
11128                   return o->multiline;//if single nothing to merge
11129                }
11130 
11131           }
11132      }
11133 
11134    return EINA_FALSE;
11135 }
11136 
11137 /**
11138  * @internal
11139  * Returns the first format in the range between start and end in the textblock
11140  * n.
11141  *
11142  * @param o the textblock object.
11143  * @param n the text node the positions refer to.
11144  * @param start the start of where to delete from.
11145  * @param end the end of the section to delete, if end == -1 it means the end of the string.
11146  */
11147 static Evas_Object_Textblock_Node_Format *
_evas_textblock_node_text_get_first_format_between(Evas_Object_Textblock_Node_Text * n,int start,int end)11148 _evas_textblock_node_text_get_first_format_between(
11149       Evas_Object_Textblock_Node_Text *n, int start, int end)
11150 {
11151    Evas_Object_Textblock_Node_Format *itr;
11152    int use_end = 1;
11153    itr = n->format_node;
11154    if (end < 0) use_end = 0;
11155    while (itr && (itr->text_node == n))
11156      {
11157         start -= itr->offset;
11158         end -= itr->offset;
11159         if ((end <= 0) && use_end)
11160           {
11161              break;
11162           }
11163         if (start <= 0)
11164           {
11165              return itr;
11166           }
11167         itr = _NODE_FORMAT(EINA_INLIST_GET(itr)->next);
11168      }
11169    return NULL;
11170 }
11171 
11172 /**
11173  * Removes a text node and the corresponding format nodes.
11174  *
11175  * @param o the textblock objec.t
11176  * @param n the node to remove.
11177  */
11178 static void
_evas_textblock_node_text_remove(Efl_Canvas_Textblock_Data * o,Evas_Object_Textblock_Node_Text * n)11179 _evas_textblock_node_text_remove(Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Node_Text *n)
11180 {
11181    _evas_textblock_node_text_adjust_offsets_to_start(o, n, 0, -1);
11182 
11183    o->text_nodes = _NODE_TEXT(eina_inlist_remove(
11184            EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n)));
11185    _evas_textblock_node_text_free(n);
11186 }
11187 
11188 /**
11189  * @internal
11190  * Return the position where the formats starts at.
11191  *
11192  * @param fmt the format to return the position of.
11193  * @return the position of the format in the text node it points to.
11194  */
11195 static size_t
_evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format * fmt)11196 _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt)
11197 {
11198    Evas_Object_Textblock_Node_Text *text;
11199    Evas_Object_Textblock_Node_Format *base_format;
11200    Evas_Object_Textblock_Node_Format *itr;
11201    size_t position = 0;
11202 
11203    if (!fmt) return 0;
11204    /* Find the main format node */
11205    text = fmt->text_node;
11206    base_format = text->format_node;
11207    EINA_INLIST_FOREACH(base_format, itr)
11208      {
11209         if (itr == fmt)
11210           {
11211              break;
11212           }
11213         position += itr->offset;
11214      }
11215    return position + fmt->offset;
11216 }
11217 
11218 EAPI int
evas_textblock_cursor_pos_get(const Efl_Text_Cursor_Handle * cur)11219 evas_textblock_cursor_pos_get(const Efl_Text_Cursor_Handle *cur)
11220 {
11221    if (!cur) return -1;
11222 
11223    Evas_Object_Textblock_Node_Text *n;
11224    size_t npos = 0;
11225    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
11226    evas_object_async_block(obj);
11227    TB_NULL_CHECK(cur->node, 0);
11228    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
11229    n = o->text_nodes;
11230    while (n != cur->node)
11231      {
11232         npos += eina_ustrbuf_length_get(n->unicode);
11233         n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
11234      }
11235    return npos + cur->pos;
11236 }
11237 
11238 EAPI void
evas_textblock_cursor_pos_set(Efl_Text_Cursor_Handle * cur,int _pos)11239 evas_textblock_cursor_pos_set(Efl_Text_Cursor_Handle *cur, int _pos)
11240 {
11241    Evas_Object_Textblock_Node_Text *n;
11242    size_t pos;
11243    if (!cur) return;
11244    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
11245    evas_object_async_block(obj);
11246    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
11247 
11248 
11249    if (_pos < 0)
11250      {
11251         pos = 0;
11252      }
11253    else
11254      {
11255         pos = (size_t) _pos;
11256      }
11257 
11258    n = o->text_nodes;
11259    while (n && (pos >= eina_ustrbuf_length_get(n->unicode)))
11260      {
11261         pos -= eina_ustrbuf_length_get(n->unicode);
11262         n = _NODE_TEXT(EINA_INLIST_GET(n)->next);
11263      }
11264 
11265    if (n)
11266      {
11267         cur->node = n;
11268         cur->pos = pos;
11269      }
11270    else if (o->text_nodes)
11271      {
11272         /* In case we went pass the last node, we need to put the cursor
11273          * at the absolute end. */
11274         Evas_Object_Textblock_Node_Text *last_n;
11275 
11276         last_n = _NODE_TEXT(EINA_INLIST_GET(o->text_nodes)->last);
11277         pos = eina_ustrbuf_length_get(last_n->unicode);
11278 
11279         cur->node = last_n;
11280         cur->pos = pos;
11281      }
11282 
11283    _evas_textblock_cursor_object_changed(cur);
11284 }
11285 
11286 EAPI Eina_Bool
evas_textblock_cursor_line_set(Evas_Textblock_Cursor * cur,int line)11287 evas_textblock_cursor_line_set(Evas_Textblock_Cursor *cur, int line)
11288 {
11289    Evas_Object_Textblock_Line *ln;
11290 
11291    if (!cur) return EINA_FALSE;
11292    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
11293    evas_object_async_block(obj);
11294 
11295    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
11296 
11297    _relayout_if_needed(cur->obj, o);
11298 
11299    ln = _find_layout_line_num(cur->obj, line);
11300    if (!ln) return EINA_FALSE;
11301 
11302    _cursor_line_first_char_get(ln, cur, o);
11303 
11304    return EINA_TRUE;
11305 }
11306 
11307 EAPI void
evas_textblock_cursor_line_jump_by(Efl_Text_Cursor_Handle * cur,int by)11308 evas_textblock_cursor_line_jump_by(Efl_Text_Cursor_Handle *cur, int by)
11309 {
11310    if (!cur) return;
11311 
11312    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
11313    ASYNC_BLOCK;
11314    int ln;
11315    Evas_Coord cx, cw;
11316    Evas_Coord lx, ly, lw, lh;
11317    int last;
11318    Evas_Object_Textblock_Node_Text *pnode;
11319    size_t ppos;
11320 
11321    ln = evas_textblock_cursor_line_geometry_get(cur, NULL, NULL, NULL, NULL) + by;
11322 
11323    pnode = cur->node;
11324    ppos = cur->pos;
11325 
11326    evas_textblock_cursor_geometry_get(cur, &cx, NULL, &cw, NULL, NULL, EVAS_TEXTBLOCK_CURSOR_BEFORE);
11327    cx += (cw / 2);
11328    evas_textblock_cursor_paragraph_last(cur);
11329    last = evas_textblock_cursor_line_geometry_get(cur, NULL, NULL, NULL, NULL);
11330 
11331    if (ln < 0)
11332      {
11333         evas_textblock_cursor_paragraph_first(cur);
11334      }
11335 
11336    else if (ln > last)
11337      {
11338         evas_textblock_cursor_paragraph_last(cur);
11339      }
11340 
11341    else
11342      {
11343         if (evas_object_textblock_line_number_geometry_get(cur->obj,
11344                  ln, &lx, &ly, &lw, &lh) &&
11345               (!evas_textblock_cursor_char_coord_set(cur, cx, ly + (lh / 2))))
11346           {
11347              evas_textblock_cursor_line_set(cur, ln);
11348              if (cx < (lx + (lw / 2)))
11349                {
11350                   if (ln == last) evas_textblock_cursor_paragraph_last(cur);
11351                   evas_textblock_cursor_line_char_first(cur);
11352                }
11353              else
11354                {
11355                   if (ln == last)
11356                      evas_textblock_cursor_paragraph_last(cur);
11357                   else
11358                      evas_textblock_cursor_line_char_last(cur);
11359                }
11360           }
11361      }
11362 
11363    if ((pnode != cur->node) || (ppos != cur->pos))
11364      {
11365         _evas_textblock_cursor_object_changed(cur);
11366      }
11367 }
11368 
11369 EAPI int
evas_textblock_cursor_compare(const Efl_Text_Cursor_Handle * cur1,const Efl_Text_Cursor_Handle * cur2)11370 evas_textblock_cursor_compare(const Efl_Text_Cursor_Handle *cur1,
11371       const Efl_Text_Cursor_Handle *cur2)
11372 {
11373    Eina_Inlist *l1, *l2;
11374 
11375    if (!cur1) return 0;
11376    if (!cur2) return 0;
11377    if (cur1->obj != cur2->obj) return 0;
11378    if ((!cur1->node) || (!cur2->node)) return 0;
11379    if (cur1->node == cur2->node)
11380      {
11381         if (cur1->pos < cur2->pos) return -1; /* cur1 < cur2 */
11382         else if (cur1->pos > cur2->pos) return 1; /* cur2 < cur1 */
11383         return 0;
11384      }
11385    for (l1 = EINA_INLIST_GET(cur1->node),
11386         l2 = EINA_INLIST_GET(cur1->node); (l1) || (l2);)
11387      {
11388         if (l1 == EINA_INLIST_GET(cur2->node)) return 1; /* cur2 < cur 1 */
11389         else if (l2 == EINA_INLIST_GET(cur2->node)) return -1; /* cur1 < cur 2 */
11390         else if (!l1) return -1; /* cur1 < cur 2 */
11391         else if (!l2) return 1; /* cur2 < cur 1 */
11392         l1 = l1->prev;
11393         l2 = l2->next;
11394      }
11395    return 0;
11396 }
11397 
11398 EAPI Eina_Bool
evas_textblock_cursor_equal(const Evas_Textblock_Cursor * cur1,const Evas_Textblock_Cursor * cur2)11399 evas_textblock_cursor_equal(const Evas_Textblock_Cursor *cur1,
11400       const Evas_Textblock_Cursor *cur2)
11401 {
11402    return ((cur1->node == cur2->node) && (cur1->pos == cur2->pos));
11403 }
11404 
11405 EAPI void
evas_textblock_cursor_copy(const Evas_Textblock_Cursor * cur_src,Efl_Text_Cursor_Handle * cur_dest)11406 evas_textblock_cursor_copy(const Evas_Textblock_Cursor *cur_src, Efl_Text_Cursor_Handle *cur_dest)
11407 {
11408    if (!cur_src || !cur_dest) return;
11409    if (!evas_textblock_cursor_equal(cur_dest, cur_src))
11410      {
11411         _evas_textblock_cursor_copy(cur_dest, cur_src);
11412      }
11413 }
11414 
11415 static void
_evas_textblock_cursor_copy(Evas_Textblock_Cursor * dst,const Efl_Text_Cursor_Handle * src)11416 _evas_textblock_cursor_copy(Evas_Textblock_Cursor *dst, const Efl_Text_Cursor_Handle *src)
11417 {
11418    if (!src) return;
11419    if (!dst) return;
11420    if (src->obj != dst->obj)
11421      {
11422         ERR("Tried copying a cursor from the wrong object");
11423         return;
11424      }
11425    dst->pos = src->pos;
11426    dst->node = src->node;
11427 }
11428 
11429 /* text controls */
11430 /**
11431  * @internal
11432  * Free a text node. Shouldn't be used usually, it's better to use
11433  * @ref _evas_textblock_node_text_remove for most cases .
11434  *
11435  * @param n the text node to free
11436  * @see _evas_textblock_node_text_remove
11437  */
11438 static void
_evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text * n)11439 _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n)
11440 {
11441    if (!n) return;
11442    eina_ustrbuf_free(n->unicode);
11443    if (n->utf8)
11444      free(n->utf8);
11445    if (n->par)
11446       n->par->text_node = NULL;
11447    free(n);
11448 }
11449 
11450 /**
11451  * @internal
11452  * Create a new text node
11453  *
11454  * @return the new text node.
11455  */
11456 static Evas_Object_Textblock_Node_Text *
_evas_textblock_node_text_new(void)11457 _evas_textblock_node_text_new(void)
11458 {
11459    Evas_Object_Textblock_Node_Text *n;
11460 
11461    n = calloc(1, sizeof(Evas_Object_Textblock_Node_Text));
11462    n->unicode = eina_ustrbuf_new();
11463    /* We want to layout each paragraph at least once. */
11464    n->dirty = EINA_TRUE;
11465    n->is_new = EINA_TRUE;
11466 
11467    return n;
11468 }
11469 
11470 /**
11471  * @internal
11472  * Break a paragraph. This does not add a PS but only splits the paragraph
11473  * where a ps was just added!
11474  *
11475  * @param cur the cursor to break at.
11476  * @param fnode the format node of the PS just added.
11477  * @return Returns no value.
11478  */
11479 static void
_evas_textblock_cursor_break_paragraph(Efl_Text_Cursor_Handle * cur,Evas_Object_Textblock_Node_Format * fnode,Eina_Bool legacy)11480 _evas_textblock_cursor_break_paragraph(Efl_Text_Cursor_Handle *cur,
11481                               Evas_Object_Textblock_Node_Format *fnode,
11482                               Eina_Bool legacy)
11483 {
11484    Evas_Object_Textblock_Node_Text *n;
11485 
11486    if (!cur) return;
11487    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
11488 
11489    n = _evas_textblock_node_text_new();
11490    o->text_nodes = _NODE_TEXT(eina_inlist_append_relative(
11491                    EINA_INLIST_GET(o->text_nodes),
11492                    EINA_INLIST_GET(n),
11493                    cur->node ? EINA_INLIST_GET(cur->node) : NULL));
11494    /* Handle text and format changes. */
11495    if (cur->node)
11496      {
11497         Evas_Object_Textblock_Node_Format *nnode;
11498         size_t len, start;
11499         const Eina_Unicode *text;
11500 
11501         if (legacy)
11502           {
11503         /* If there was a format node in the delete range,
11504          * make it our format and update the text_node fields,
11505          * otherwise, use the paragraph separator
11506          * of the previous paragraph. */
11507              nnode  = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
11508              if (nnode && (nnode->text_node == cur->node))
11509                {
11510                   n->format_node = nnode;
11511                   nnode->offset--; /* We don't have to take the replacement char
11512                                       into account anymore */
11513                   while (nnode && (nnode->text_node == cur->node))
11514                     {
11515                        nnode->text_node = n;
11516                        nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next);
11517                     }
11518                }
11519              else
11520                {
11521                   n->format_node = fnode;
11522                }
11523           }
11524         else
11525           {
11526              n->format_node = fnode;
11527           }
11528 
11529         start = cur->pos;
11530         if (legacy)
11531           {
11532              /* cur->pos now points to the PS, move after. */
11533              start++;
11534           }
11535         len = eina_ustrbuf_length_get(cur->node->unicode) - start;
11536         if (len > 0)
11537           {
11538              text = eina_ustrbuf_string_get(cur->node->unicode);
11539              eina_ustrbuf_append_length(n->unicode, text + start, len);
11540              eina_ustrbuf_remove(cur->node->unicode, start, start + len);
11541              cur->node->dirty = EINA_TRUE;
11542           }
11543      }
11544    else if (!cur->node)
11545      {
11546         fnode = o->format_nodes;
11547         if (fnode)
11548           {
11549              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->last);
11550           }
11551         n->format_node = fnode;
11552      }
11553 }
11554 
11555 /**
11556  * @internal
11557  * Set the node and offset of all the curs after cur.
11558  *
11559  * @param cur the cursor.
11560  * @param n the current textblock node.
11561  * @param new_node the new node to set.
11562  */
11563 static void
_evas_textblock_cursors_set_node(Efl_Canvas_Textblock_Data * o,const Evas_Object_Textblock_Node_Text * n,Evas_Object_Textblock_Node_Text * new_node)11564 _evas_textblock_cursors_set_node(Efl_Canvas_Textblock_Data *o,
11565       const Evas_Object_Textblock_Node_Text *n,
11566       Evas_Object_Textblock_Node_Text *new_node)
11567 {
11568    Eina_List *l;
11569    Efl_Text_Cursor_Handle *cur = o->cursor;
11570    Efl_Text_Cursor_Handle *data;
11571 
11572    if (n == cur->node)
11573      {
11574         cur->pos = 0;
11575         cur->node = new_node;
11576         cur->changed = EINA_TRUE;
11577      }
11578    EINA_LIST_FOREACH(o->cursors, l, data)
11579      {
11580         if (n == data->node)
11581           {
11582              data->pos = 0;
11583              data->node = new_node;
11584              data->changed = EINA_TRUE;
11585           }
11586      }
11587 }
11588 
11589 static inline void
_cursor_update_offset(Efl_Text_Cursor_Handle * cur,Efl_Canvas_Textblock_Data * o,const Evas_Object_Textblock_Node_Text * n,size_t start,int offset)11590 _cursor_update_offset(Efl_Text_Cursor_Handle *cur, Efl_Canvas_Textblock_Data *o,
11591       const Evas_Object_Textblock_Node_Text *n, size_t start, int offset)
11592 {
11593    if ((n == cur->node) &&
11594          (cur->pos > start))
11595      {
11596         if ((offset < 0) && (cur->pos <= (size_t) (-1 * offset)))
11597           {
11598              cur->pos = 0;
11599           }
11600         else
11601           {
11602              cur->pos += offset;
11603           }
11604         cur->changed = EINA_TRUE;
11605      }
11606    else if (!cur->node)
11607      {
11608         cur->node = o->text_nodes;
11609         cur->pos = 0;
11610         cur->changed = EINA_TRUE;
11611      }
11612 }
11613 
11614 /**
11615  * @internal
11616  * Update the offset of all the cursors after cur.
11617  *
11618  * @param cur the cursor.
11619  * @param n the current textblock node.
11620  * @param start the starting pos.
11621  * @param offset how much to adjust (can be negative).
11622  */
11623 static void
_evas_textblock_cursors_update_offset(const Efl_Text_Cursor_Handle * cur,const Evas_Object_Textblock_Node_Text * n,size_t start,int offset)11624 _evas_textblock_cursors_update_offset(const Efl_Text_Cursor_Handle *cur,
11625       const Evas_Object_Textblock_Node_Text *n,
11626       size_t start, int offset)
11627 {
11628    Eina_List *l;
11629    Efl_Text_Cursor_Handle *ocur;
11630    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
11631 
11632    ocur = o->cursor;
11633    if (cur != ocur)
11634      {
11635         _cursor_update_offset(ocur, o, n, start, offset);
11636      }
11637 
11638    EINA_LIST_FOREACH(o->cursors, l, ocur)
11639      {
11640         if (ocur != cur)
11641           {
11642              _cursor_update_offset(ocur, o, n, start, offset);
11643           }
11644      }
11645 }
11646 
11647 /**
11648  * @internal
11649  * Mark that the textblock has changed.
11650  *
11651  * @param o the textblock object.
11652  * @param obj the evas object.
11653  */
11654 static void
_evas_textblock_changed(Efl_Canvas_Textblock_Data * o,Evas_Object * eo_obj)11655 _evas_textblock_changed(Efl_Canvas_Textblock_Data *o, Evas_Object *eo_obj)
11656 {
11657    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
11658    Eina_List *l;
11659    Efl_Text_Cursor_Handle *data_obj;
11660    LYDBG("ZZ: invalidate 1 %p\n", eo_obj);
11661    o->formatted.valid = 0;
11662    o->native.valid = 0;
11663    o->content_changed = 1;
11664    if (!fit_is_fitting(eo_obj))
11665      {
11666         if (o->markup_text)
11667           {
11668              eina_stringshare_del(o->markup_text);
11669              o->markup_text = NULL;
11670           }
11671      }
11672    else
11673      {
11674         _evas_clear_main_format(eo_obj, o);
11675      }
11676 
11677    // FIXME: emit ONCE after this following checks
11678    _cursor_emit_if_changed(o->cursor);
11679    EINA_LIST_FOREACH(o->cursors, l, data_obj)
11680      {
11681         _cursor_emit_if_changed(data_obj);
11682      }
11683 
11684    /*
11685       If format changed we need to refit content again.
11686       If content already fitting then ignore fitting (fitting cause fall to this callback)
11687    */
11688    if (!fit_is_fitting(eo_obj) && o->fit_content_config.options != TEXTBLOCK_FIT_MODE_NONE)
11689      {
11690         fit_cache_clear(&o->fit_content_config, FIT_CACHE_ALL);
11691         fit_text_block(eo_obj);
11692      }
11693    evas_object_change(eo_obj, obj);
11694 }
11695 
11696 static void
_evas_textblock_invalidate_all(Efl_Canvas_Textblock_Data * o)11697 _evas_textblock_invalidate_all(Efl_Canvas_Textblock_Data *o)
11698 {
11699    Evas_Object_Textblock_Node_Text *n;
11700 
11701    EINA_INLIST_FOREACH(o->text_nodes, n)
11702      {
11703         n->dirty = EINA_TRUE;
11704      }
11705 }
11706 
11707 static int
_evas_textblock_cursor_text_append(Efl_Text_Cursor_Handle * cur,const char * _text)11708 _evas_textblock_cursor_text_append(Efl_Text_Cursor_Handle *cur, const char *_text)
11709 {
11710    Evas_Object_Textblock_Node_Text *n;
11711    Evas_Object_Textblock_Node_Format *fnode = NULL;
11712    Eina_Unicode *text;
11713    Efl_Text_Cursor_Handle *main_cur;
11714    int len = 0;
11715 
11716    if (!cur) return 0;
11717    if (!_text || !(*_text)) return 0;
11718    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
11719    evas_object_async_block(obj);
11720    text = eina_unicode_utf8_to_unicode(_text, &len);
11721    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
11722 
11723    n = cur->node;
11724    if (n)
11725      {
11726         Evas_Object_Textblock_Node_Format *nnode;
11727         fnode = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
11728         fnode = _evas_textblock_node_format_last_at_off(fnode);
11729         /* find the node after the current in the same paragraph
11730          * either we find one and then take the next, or we try to get
11731          * the first for the paragraph which must be after our position  */
11732         if (fnode)
11733           {
11734              if (!_evas_textblock_cursor_format_is_visible_get(cur))
11735                {
11736                   nnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
11737                   if (nnode && (nnode->text_node == n))
11738                     {
11739                        fnode = nnode;
11740                     }
11741                   else
11742                     {
11743                        fnode = NULL;
11744                     }
11745                }
11746           }
11747         else
11748           {
11749              fnode = n->format_node;
11750           }
11751      }
11752    else if (o->text_nodes)
11753      {
11754         n = cur->node = o->text_nodes;
11755         cur->pos = 0;
11756         cur->changed = EINA_TRUE;
11757      }
11758    else
11759      {
11760         n = _evas_textblock_node_text_new();
11761         o->text_nodes = _NODE_TEXT(eina_inlist_append(
11762                    EINA_INLIST_GET(o->text_nodes),
11763                    EINA_INLIST_GET(n)));
11764         cur->node = n;
11765         cur->changed = EINA_TRUE;
11766      }
11767 
11768    eina_ustrbuf_insert_length(n->unicode, text, len, cur->pos);
11769    /* Advance the formats */
11770    if (fnode && (fnode->text_node == cur->node))
11771      fnode->offset += len;
11772 
11773    /* Update all the cursors after our position. */
11774    _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, len);
11775 
11776    if (!o->pause_change)
11777      {
11778         _evas_textblock_changed(o, cur->obj);
11779         efl_event_callback_call(cur->obj, EFL_CANVAS_TEXTBLOCK_EVENT_CHANGED, NULL);
11780      }
11781    n->dirty = EINA_TRUE;
11782    free(text);
11783 
11784    main_cur = o->cursor;
11785    if (!main_cur->node)
11786       main_cur->node = o->text_nodes;
11787 
11788    _evas_textblock_cursor_object_changed(cur);
11789    return len;
11790 }
11791 
11792 EAPI int
evas_textblock_cursor_text_append(Evas_Textblock_Cursor * cur,const char * _text)11793 evas_textblock_cursor_text_append(Evas_Textblock_Cursor *cur, const char *_text)
11794 {
11795    return _evas_textblock_cursor_text_append(cur, _text);
11796 }
11797 
11798 static int
_evas_textblock_cursor_text_prepend(Efl_Text_Cursor_Handle * cur,const char * _text)11799 _evas_textblock_cursor_text_prepend(Efl_Text_Cursor_Handle *cur, const char *_text)
11800 {
11801    int len;
11802    /*append is essentially prepend without advancing */
11803    if (!cur) return 0;
11804    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
11805    evas_object_async_block(obj);
11806    len = _evas_textblock_cursor_text_append(cur, _text);
11807    if (len == 0) return 0;
11808    cur->pos += len; /*Advance */
11809    return len;
11810 }
11811 
11812 EAPI int
evas_textblock_cursor_text_prepend(Efl_Text_Cursor_Handle * cur,const char * _text)11813 evas_textblock_cursor_text_prepend(Efl_Text_Cursor_Handle *cur, const char *_text)
11814 {
11815    return _evas_textblock_cursor_text_prepend(cur, _text);
11816 }
11817 
11818 /**
11819  * @internal
11820  * Free a format node
11821  *
11822  * @param o the textblock object
11823  * @param n the format node to free
11824  */
11825 static void
_evas_textblock_node_format_free(Efl_Canvas_Textblock_Data * o,Evas_Object_Textblock_Node_Format * n)11826 _evas_textblock_node_format_free(Efl_Canvas_Textblock_Data *o,
11827       Evas_Object_Textblock_Node_Format *n)
11828 {
11829    if (!n) return;
11830    eina_stringshare_del(n->format);
11831    eina_stringshare_del(n->orig_format);
11832    if (n->anchor == ANCHOR_ITEM)
11833       o->anchors_item = eina_list_remove(o->anchors_item, n);
11834    else if (n->anchor == ANCHOR_A)
11835       o->anchors_a = eina_list_remove(o->anchors_a, n);
11836    free(n);
11837 }
11838 
11839 /**
11840  * @internal
11841  * Create a new format node.
11842  *
11843  * @param format the text to create the format node from.
11844  * @param o the textblock object.
11845  * @return Returns the new format node
11846  */
11847 static Evas_Object_Textblock_Node_Format *
_evas_textblock_node_format_new(Efl_Canvas_Textblock_Data * o,const char * _format,Eina_Bool is_item)11848 _evas_textblock_node_format_new(Efl_Canvas_Textblock_Data *o, const char *_format,
11849       Eina_Bool is_item)
11850 {
11851    Evas_Object_Textblock_Node_Format *n;
11852    const char *format = _format;
11853    const char *pre_stripped_format = NULL;
11854 
11855    n = calloc(1, sizeof(Evas_Object_Textblock_Node_Format));
11856    /* Create orig_format and format */
11857    if (format[0] == '<')
11858      {
11859         const char *match;
11860         size_t format_len;
11861 
11862         format++; /* Advance after '<' */
11863         format_len = strlen(format);
11864         if ((format_len > 0) && format[format_len - 1] == '>')
11865           {
11866              format_len--; /* We don't care about '>' */
11867              n->orig_format = eina_stringshare_add_length(format, format_len);
11868              /* Check if it closes itself, i.e. one of the following:
11869               * 1. Ends with a "/" e.g. "<my_tag/>"
11870               * 2. Is a paragraph separator */
11871              /* Skip the </> case. */
11872              if (format_len > 1)
11873                {
11874                   if (format[format_len - 1] == '/')
11875                     {
11876                        format_len--; /* We don't care about '/' */
11877                        n->own_closer = EINA_TRUE;
11878                     }
11879                   else if (format_len < 4)
11880                     {
11881                        /* br,ps,tab are already own_closer without '/' */
11882                        char tmp[format_len + 1];
11883                        strncpy(tmp, format, format_len);
11884                        tmp[format_len] = '\0';
11885                        if (_IS_PARAGRAPH_SEPARATOR(o, tmp) ||
11886                            _IS_LINE_SEPARATOR(tmp) ||
11887                            _IS_TAB(tmp))
11888                          {
11889                              n->own_closer = EINA_TRUE;
11890                          }
11891                     }
11892                }
11893           }
11894 
11895         match = _textblock_format_node_from_style_tag(o, n, format, format_len);
11896 
11897         if (match)
11898           {
11899              pre_stripped_format = match;
11900           }
11901         else
11902           {
11903              if (format[0] == '/')
11904                {
11905                   format++;
11906                   format_len--;
11907                }
11908              else
11909                {
11910                   n->opener = EINA_TRUE;
11911                }
11912           }
11913 
11914         if (!pre_stripped_format)
11915            pre_stripped_format = n->orig_format;
11916      }
11917    /* Just use as is, it's a special format. */
11918    else
11919      {
11920         const char *tmp = format;
11921         if (format[0] != '-')
11922           {
11923              n->opener = EINA_TRUE;
11924              if (format[0] != '+')
11925                {
11926                   n->own_closer = EINA_TRUE;
11927                }
11928           }
11929         if ((*tmp == '+') || (*tmp == '-'))
11930           {
11931              tmp++;
11932              while (*tmp == ' ') tmp++;
11933           }
11934         n->orig_format = eina_stringshare_add(tmp);
11935         pre_stripped_format = n->orig_format;
11936      }
11937 
11938    /* Strip format */
11939      {
11940         const char *tmp = pre_stripped_format;
11941         int len = strlen(tmp);
11942         if ((*tmp == '+') || (*tmp == '-'))
11943           {
11944              len--;
11945              tmp++;
11946              while (*tmp == ' ') tmp++;
11947           }
11948         if (tmp[len - 1] == '/')
11949           {
11950              len--;
11951           }
11952         else if (tmp[0] == '/')
11953           {
11954              len--;
11955              tmp++;
11956           }
11957         n->format = eina_stringshare_add_length(tmp, len);
11958      }
11959    format = n->format;
11960 
11961    _evas_textblock_format_is_visible(n, format, is_item);
11962    if (n->anchor == ANCHOR_A)
11963      {
11964         o->anchors_a = eina_list_append(o->anchors_a, n);
11965      }
11966    else if (n->anchor == ANCHOR_ITEM)
11967      {
11968         o->anchors_item = eina_list_append(o->anchors_item, n);
11969      }
11970    n->is_new = EINA_TRUE;
11971 
11972    return n;
11973 }
11974 
11975 static Eina_Bool
_evas_textblock_cursor_is_at_the_end(const Efl_Text_Cursor_Handle * cur)11976 _evas_textblock_cursor_is_at_the_end(const Efl_Text_Cursor_Handle *cur)
11977 {
11978    const Eina_Unicode *text;
11979 
11980    if (!cur) return EINA_FALSE;
11981    TB_NULL_CHECK(cur->node, EINA_FALSE);
11982    text = eina_ustrbuf_string_get(cur->node->unicode);
11983    if ((cur->pos - 1) > eina_ustrbuf_length_get(cur->node->unicode)) return EINA_FALSE;
11984    return ((text[cur->pos] == 0) && (!EINA_INLIST_GET(cur->node)->next)) ?
11985               EINA_TRUE : EINA_FALSE;
11986 }
11987 
11988 static Eina_Bool
_evas_textblock_cursor_format_append(Efl_Text_Cursor_Handle * cur,const char * format,Evas_Object_Textblock_Node_Format ** _fnode,Eina_Bool is_item)11989 _evas_textblock_cursor_format_append(Efl_Text_Cursor_Handle *cur,
11990       const char *format, Evas_Object_Textblock_Node_Format **_fnode,
11991       Eina_Bool is_item)
11992 {
11993    Evas_Object_Textblock_Node_Format *n;
11994    Eina_Bool is_visible;
11995 
11996    if (!cur) return EINA_FALSE;
11997    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
11998    evas_object_async_block(obj);
11999    if ((!format) || (format[0] == 0)) return EINA_FALSE;
12000    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
12001    /* We should always have at least one text node */
12002    if (!o->text_nodes)
12003      {
12004         _evas_textblock_cursor_text_prepend(cur, "");
12005      }
12006 
12007    n = _evas_textblock_node_format_new(o, format, is_item);
12008    is_visible = n->visible;
12009    format = n->format;
12010    if (!cur->node)
12011      {
12012         o->format_nodes = _NODE_FORMAT(eina_inlist_append(
12013                  EINA_INLIST_GET(o->format_nodes),
12014                  EINA_INLIST_GET(n)));
12015         cur->pos = 0;
12016         n->text_node = (EINA_INLIST_GET(n)->prev) ?
12017            _NODE_FORMAT(EINA_INLIST_GET(n)->prev)->text_node :
12018            o->text_nodes;
12019         cur->node = n->text_node;
12020         cur->changed = EINA_TRUE;
12021      }
12022    else
12023      {
12024         Evas_Object_Textblock_Node_Format *fmt;
12025         fmt = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
12026         n->text_node = cur->node;
12027         if (!fmt)
12028           {
12029              o->format_nodes = _NODE_FORMAT(eina_inlist_prepend(
12030                       EINA_INLIST_GET(o->format_nodes),
12031                       EINA_INLIST_GET(n)));
12032              n->offset = cur->pos;
12033           }
12034         else
12035           {
12036              fmt = _evas_textblock_node_format_last_at_off(fmt);
12037              if (_evas_textblock_cursor_format_is_visible_get(cur))
12038                {
12039                   o->format_nodes = _NODE_FORMAT(eina_inlist_prepend_relative(
12040                            EINA_INLIST_GET(o->format_nodes),
12041                            EINA_INLIST_GET(n),
12042                            EINA_INLIST_GET(fmt)
12043                            ));
12044                   n->offset = fmt->offset;
12045                   if (fmt->text_node->format_node == fmt)
12046                     {
12047                        fmt->text_node->format_node = n;
12048                     }
12049                }
12050              else
12051                {
12052                   o->format_nodes = _NODE_FORMAT(eina_inlist_append_relative(
12053                            EINA_INLIST_GET(o->format_nodes),
12054                            EINA_INLIST_GET(n),
12055                            EINA_INLIST_GET(fmt)
12056                            ));
12057                   if (fmt->text_node != cur->node)
12058                     {
12059                        n->offset = cur->pos;
12060                     }
12061                   else
12062                     {
12063                        n->offset = cur->pos -
12064                           _evas_textblock_node_format_pos_get(fmt);
12065                     }
12066                }
12067           }
12068         /* Adjust differently if we insert a format char */
12069         if (is_visible)
12070           {
12071              _evas_textblock_node_format_adjust_offset(o, cur->node, n,
12072                    -(n->offset - 1));
12073           }
12074         else
12075           {
12076              _evas_textblock_node_format_adjust_offset(o, cur->node, n,
12077                    -n->offset);
12078           }
12079 
12080         if (!fmt || (fmt->text_node != cur->node))
12081           {
12082              cur->node->format_node = n;
12083           }
12084      }
12085    if (is_visible && cur->node)
12086      {
12087         Eina_Unicode insert_char;
12088         /* Insert a visual representation according to the type of the
12089            format */
12090         if (_IS_PARAGRAPH_SEPARATOR_SIMPLE(format))
12091            insert_char = _PARAGRAPH_SEPARATOR;
12092         else if (_IS_LINE_SEPARATOR(format))
12093            insert_char = _NEWLINE;
12094         else if (_IS_TAB(format))
12095            insert_char = _TAB;
12096         else
12097            insert_char = _REPLACEMENT_CHAR;
12098 
12099         eina_ustrbuf_insert_char(cur->node->unicode, insert_char, cur->pos);
12100 
12101         /* Advance all the cursors after our cursor */
12102         _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, 1);
12103         if (_IS_PARAGRAPH_SEPARATOR(o, format))
12104           {
12105              if (o->multiline)
12106                 _evas_textblock_cursor_break_paragraph(cur, n, EINA_TRUE);
12107           }
12108         else
12109           {
12110              /* Handle visible format nodes here */
12111              cur->node->dirty = EINA_TRUE;
12112              n->is_new = EINA_FALSE;
12113           }
12114      }
12115    else
12116      {
12117         o->format_changed = EINA_TRUE;
12118      }
12119 
12120    if (!o->pause_change)
12121      _evas_textblock_changed(o, cur->obj);
12122 
12123    Efl_Text_Cursor_Handle *ocur = o->cursor;
12124    if (!ocur->node)
12125       ocur->node = o->text_nodes;
12126 
12127    if (_fnode) *_fnode = n;
12128    return is_visible;
12129 }
12130 
12131 EAPI Eina_Bool
evas_textblock_cursor_format_append(Evas_Textblock_Cursor * cur,const char * format)12132 evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, const char *format)
12133 {
12134    return _evas_textblock_cursor_format_append(cur, format, NULL, EINA_FALSE);
12135 }
12136 
12137 static Eina_Bool
_evas_textblock_cursor_format_prepend(Efl_Text_Cursor_Handle * cur,const char * format)12138 _evas_textblock_cursor_format_prepend(Efl_Text_Cursor_Handle *cur, const char *format)
12139 {
12140    Eina_Bool is_visible;
12141 
12142    if (!cur) return EINA_FALSE;
12143    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
12144    evas_object_async_block(obj);
12145    /* append is essentially prepend without advancing */
12146    is_visible = _evas_textblock_cursor_format_append(cur, format, NULL, EINA_FALSE);
12147    if (is_visible)
12148      {
12149         /* Advance after the replacement char */
12150         evas_textblock_cursor_char_next(cur);
12151      }
12152 
12153    return is_visible;
12154 }
12155 
12156 EAPI Eina_Bool
evas_textblock_cursor_format_prepend(Evas_Textblock_Cursor * cur,const char * format)12157 evas_textblock_cursor_format_prepend(Evas_Textblock_Cursor *cur, const char *format)
12158 {
12159    return _evas_textblock_cursor_format_prepend(cur, format);
12160 }
12161 
12162 EAPI void
evas_textblock_cursor_char_delete(Efl_Text_Cursor_Handle * cur)12163 evas_textblock_cursor_char_delete(Efl_Text_Cursor_Handle *cur)
12164 {
12165    Evas_Object_Textblock_Node_Text *n, *n2;
12166    const Eina_Unicode *text;
12167    int chr, ind, ppos;
12168 
12169    if (!cur || !cur->node) return;
12170    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
12171    evas_object_async_block(obj);
12172    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
12173    n = cur->node;
12174 
12175    text = eina_ustrbuf_string_get(n->unicode);
12176    ind = cur->pos;
12177    if (text[ind])
12178       chr = text[ind++];
12179    else
12180       chr = 0;
12181 
12182    if (chr == 0) return;
12183    ppos = cur->pos;
12184    eina_ustrbuf_remove(n->unicode, cur->pos, ind);
12185    /* Remove a format node if needed, and remove the char only if the
12186     * fmt node is not visible */
12187      {
12188         Eina_Bool should_merge = EINA_FALSE;
12189         Evas_Object_Textblock_Node_Format *fmt, *fmt2;
12190         fmt = _evas_textblock_cursor_node_format_at_pos_get(cur);
12191         if (fmt)
12192           {
12193              const char *format = NULL;
12194              Evas_Object_Textblock_Node_Format *last_fmt;
12195              /* If there's a PS it must be the last become it delimits paragraphs */
12196              last_fmt = _evas_textblock_node_format_last_at_off(fmt);
12197              format = last_fmt->format;
12198              if (format && _IS_PARAGRAPH_SEPARATOR(o, format))
12199                {
12200                   /* If it was a paragraph separator, we should merge the
12201                    * current with the next, there must be a next. */
12202                   should_merge = EINA_TRUE;
12203                }
12204              /* If a singular, mark as invisible, so we'll delete it. */
12205              if (!format || last_fmt->own_closer)
12206                {
12207                   last_fmt->visible = EINA_FALSE;
12208                }
12209           }
12210 
12211         fmt2 = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur);
12212         fmt2 = _evas_textblock_node_format_last_at_off(fmt2);
12213         _evas_textblock_node_format_adjust_offset(o, cur->node, fmt2,
12214               -(ind - cur->pos));
12215 
12216         if (should_merge)
12217           {
12218              _evas_textblock_cursor_nodes_merge(cur);
12219           }
12220 
12221         _evas_textblock_node_format_remove_matching(o, fmt);
12222      }
12223 
12224    if (cur->pos == eina_ustrbuf_length_get(n->unicode))
12225      {
12226         n2 = _NODE_TEXT(EINA_INLIST_GET(n)->next);
12227         if (n2)
12228           {
12229              cur->node = n2;
12230              cur->pos = 0;
12231              cur->changed = EINA_TRUE;
12232           }
12233      }
12234 
12235    _evas_textblock_cursors_update_offset(cur, n, ppos, -(ind - ppos));
12236    _evas_textblock_changed(o, cur->obj);
12237    cur->node->dirty = EINA_TRUE;
12238    _evas_textblock_cursor_object_changed(cur);
12239 }
12240 
12241 EAPI void
evas_textblock_cursor_range_delete(Efl_Text_Cursor_Handle * cur1,Efl_Text_Cursor_Handle * cur2)12242 evas_textblock_cursor_range_delete(Efl_Text_Cursor_Handle *cur1, Efl_Text_Cursor_Handle *cur2)
12243 {
12244    if (!cur1) return;
12245    Evas_Object_Textblock_Node_Format *fnode = NULL;
12246    Evas_Object_Textblock_Node_Text *n1, *n2;
12247    Eina_Bool should_merge = EINA_FALSE, reset_cursor = EINA_FALSE;
12248 
12249    if (!cur1 || !cur1->node) return;
12250    if (!cur2 || !cur2->node) return;
12251    if (cur1->obj != cur2->obj) return;
12252    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur1->obj, EFL_CANVAS_OBJECT_CLASS);
12253    evas_object_async_block(obj);
12254    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur1->obj, MY_CLASS);
12255    if (evas_textblock_cursor_compare(cur1, cur2) > 0)
12256      {
12257         Efl_Text_Cursor_Handle *tc;
12258 
12259         tc = cur1;
12260         cur1 = cur2;
12261         cur2 = tc;
12262      }
12263    n1 = cur1->node;
12264    n2 = cur2->node;
12265    if ((evas_textblock_cursor_compare(o->cursor, cur1) >= 0) &&
12266          (evas_textblock_cursor_compare(cur2, o->cursor) >= 0))
12267      {
12268         reset_cursor = EINA_TRUE;
12269      }
12270 
12271    if (n1 == n2)
12272      {
12273         if ((cur1->pos == 0) &&
12274               (cur2->pos == eina_ustrbuf_length_get(n1->unicode)))
12275           {
12276              /* Remove the whole node. */
12277              Evas_Object_Textblock_Node_Text *n =
12278                 _NODE_TEXT(EINA_INLIST_GET(n1)->next);
12279              if (n)
12280                {
12281                   should_merge = EINA_TRUE;
12282                }
12283              _evas_textblock_node_text_adjust_offsets_to_start(o, n1, cur1->pos, -1);
12284           }
12285         else
12286           {
12287              should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o,
12288                    n1, cur1->pos, cur2->pos);
12289           }
12290         eina_ustrbuf_remove(n1->unicode, cur1->pos, cur2->pos);
12291         _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos, - (cur2->pos - cur1->pos));
12292      }
12293    else
12294      {
12295         Evas_Object_Textblock_Node_Text *n;
12296         int len;
12297         n = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
12298         /* Remove all the text nodes between */
12299         while (n && (n != n2))
12300           {
12301              Evas_Object_Textblock_Node_Text *nnode;
12302 
12303              nnode = _NODE_TEXT(EINA_INLIST_GET(n)->next);
12304              _evas_textblock_nodes_merge(o, n1);
12305              n = nnode;
12306           }
12307         /* After we merged all the nodes, move the formats to the start of
12308          * the range. */
12309         _evas_textblock_node_text_adjust_offsets_to_start(o, n1, cur1->pos, -1);
12310 
12311         should_merge = _evas_textblock_node_text_adjust_offsets_to_start(o, n2,
12312               0, cur2->pos);
12313 
12314         /* Remove the formats and the strings in the first and last nodes */
12315         len = eina_ustrbuf_length_get(n1->unicode);
12316         eina_ustrbuf_remove(n1->unicode, cur1->pos, len);
12317         eina_ustrbuf_remove(n2->unicode, 0, cur2->pos);
12318         /* Merge the nodes because we removed the PS */
12319         _evas_textblock_cursors_update_offset(cur1, cur1->node, cur1->pos,
12320                                               -cur1->pos);
12321         _evas_textblock_cursors_update_offset(cur2, cur2->node, 0, -cur2->pos);
12322         cur2->pos = 0;
12323         cur2->changed = EINA_TRUE;
12324         _evas_textblock_nodes_merge(o, n1);
12325      }
12326    fnode = _evas_textblock_cursor_node_format_at_pos_get(cur1);
12327 
12328    n1 = cur1->node;
12329    n2 = cur2->node;
12330    n1->dirty = n2->dirty = EINA_TRUE;
12331 
12332    if (should_merge)
12333      {
12334         /* We call this function instead of the cursor one because we already
12335          * updated the cursors */
12336         _evas_textblock_nodes_merge(o, n1);
12337      }
12338    _evas_textblock_node_format_remove_matching(o, fnode);
12339 
12340    evas_textblock_cursor_copy(cur1, cur2);
12341    if (reset_cursor)
12342      evas_textblock_cursor_copy(cur1, o->cursor);
12343 
12344    _evas_textblock_changed(o, cur1->obj);
12345    efl_event_callback_call(cur1->obj, EFL_CANVAS_TEXTBLOCK_EVENT_CHANGED, NULL);
12346 }
12347 
12348 EAPI char *
evas_textblock_cursor_content_get(const Evas_Textblock_Cursor * cur)12349 evas_textblock_cursor_content_get(const Evas_Textblock_Cursor *cur)
12350 {
12351    if (!cur || !cur->node) return NULL;
12352    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
12353    evas_object_async_block(obj);
12354    if (evas_textblock_cursor_format_is_visible_get(cur))
12355      {
12356         Eina_Strbuf *buf;
12357         Evas_Object_Textblock_Node_Format *fnode;
12358         char *ret;
12359         fnode = _evas_textblock_node_visible_at_pos_get(
12360                  evas_textblock_cursor_format_get(cur));
12361 
12362         buf = eina_strbuf_new();
12363         _markup_get_format_append(buf, fnode);
12364         ret = eina_strbuf_string_steal(buf);
12365         eina_strbuf_free(buf);
12366 
12367         return ret;
12368      }
12369    else
12370      {
12371         Eina_Unicode buf[2];
12372         char *s;
12373 
12374         buf[0] = eina_ustrbuf_string_get(cur->node->unicode)[cur->pos];
12375         buf[1] = 0;
12376         s = eina_unicode_unicode_to_utf8(buf, NULL);
12377 
12378         return s;
12379      }
12380 }
12381 
12382 static char *
_evas_textblock_cursor_range_text_markup_get(const Efl_Text_Cursor_Handle * cur1,const Efl_Text_Cursor_Handle * _cur2)12383 _evas_textblock_cursor_range_text_markup_get(const Efl_Text_Cursor_Handle *cur1, const Efl_Text_Cursor_Handle *_cur2)
12384 {
12385    Evas_Object_Textblock_Node_Text *tnode;
12386    Eina_Strbuf *buf;
12387    Efl_Text_Cursor_Handle *cur2;
12388 
12389    buf = eina_strbuf_new();
12390 
12391    if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
12392      {
12393         const Efl_Text_Cursor_Handle *tc;
12394 
12395         tc = cur1;
12396         cur1 = _cur2;
12397         _cur2 = tc;
12398      }
12399    /* Work on a local copy of the cur */
12400    cur2 = alloca(sizeof(Efl_Text_Cursor_Handle));
12401    cur2->obj = _cur2->obj;
12402    _evas_textblock_cursor_copy(cur2, _cur2);
12403 
12404    /* Parse the text between the cursors. */
12405    for (tnode = cur1->node ; tnode ;
12406          tnode = _NODE_TEXT(EINA_INLIST_GET(tnode)->next))
12407      {
12408         Evas_Object_Textblock_Node_Format *fnode;
12409         Eina_Unicode *text_base, *text;
12410         int cur1_pos = 0, cur2_pos = -1;
12411         int off = 0;
12412 
12413         text_base = text =
12414            eina_unicode_strndup(eina_ustrbuf_string_get(tnode->unicode),
12415                                 eina_ustrbuf_length_get(tnode->unicode));
12416         if (tnode == cur2->node)
12417           cur2_pos = cur2->pos;
12418         if (tnode == cur1->node)
12419           cur1_pos = cur1->pos;
12420         fnode = _evas_textblock_node_text_get_first_format_between(tnode,
12421                 cur1_pos, cur2_pos);
12422         /* Init the offset so the first one will count starting from cur1->pos
12423          * and not the previous format node */
12424         if (tnode == cur1->node)
12425           {
12426              if (fnode)
12427                {
12428                   off = _evas_textblock_node_format_pos_get(fnode) -
12429                      cur1->pos - fnode->offset;
12430                }
12431              text += cur1->pos;
12432           }
12433         else
12434           {
12435              off = 0;
12436           }
12437         while (fnode && (fnode->text_node == tnode))
12438           {
12439              Eina_Unicode tmp_ch;
12440              off += fnode->offset;
12441              if ((tnode == cur2->node) &&
12442                    ((size_t) (text - text_base + off) >= cur2->pos))
12443                {
12444                   break;
12445                }
12446              /* No need to skip on the first run */
12447              tmp_ch = text[off];
12448              text[off] = 0; /* Null terminate the part of the string */
12449              _markup_get_text_append(buf, text);
12450              _markup_get_format_append(buf, fnode);
12451              text[off] = tmp_ch; /* Restore the char */
12452              text += off;
12453              if (fnode->visible)
12454                {
12455                   off = -1;
12456                   text++;
12457                }
12458              else
12459                {
12460                   off = 0;
12461                }
12462              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
12463           }
12464         /* If we got to the last node, stop and add the rest outside */
12465         if (cur2->node == tnode)
12466           {
12467              /* Add the rest, skip replacement */
12468              /* Don't go past the second cursor pos */
12469              text_base[cur2->pos] = '\0';
12470              _markup_get_text_append(buf, text);
12471              free(text_base);
12472              break;
12473           }
12474         else
12475           {
12476              /* Add the rest, skip replacement */
12477              _markup_get_text_append(buf, text);
12478              free(text_base);
12479           }
12480      }
12481    /* return the string */
12482      {
12483         char *ret;
12484         ret = eina_strbuf_string_steal(buf);
12485         eina_strbuf_free(buf);
12486         return ret;
12487      }
12488 }
12489 
12490 static char *
_evas_textblock_cursor_range_text_plain_get(const Efl_Text_Cursor_Handle * cur1,const Efl_Text_Cursor_Handle * _cur2)12491 _evas_textblock_cursor_range_text_plain_get(const Efl_Text_Cursor_Handle *cur1, const Efl_Text_Cursor_Handle *_cur2)
12492 {
12493    Eina_UStrbuf *buf;
12494    Evas_Object_Textblock_Node_Text *n1, *n2;
12495    Efl_Text_Cursor_Handle *cur2;
12496 
12497    buf = eina_ustrbuf_new();
12498 
12499    if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
12500      {
12501         const Efl_Text_Cursor_Handle *tc;
12502 
12503         tc = cur1;
12504         cur1 = _cur2;
12505         _cur2 = tc;
12506      }
12507    n1 = cur1->node;
12508    n2 = _cur2->node;
12509    /* Work on a local copy of the cur */
12510    cur2 = alloca(sizeof(Efl_Text_Cursor_Handle));
12511    cur2->obj = _cur2->obj;
12512    _evas_textblock_cursor_copy(cur2, _cur2);
12513 
12514    if (n1 == n2)
12515      {
12516         const Eina_Unicode *tmp;
12517         tmp = eina_ustrbuf_string_get(n1->unicode);
12518         eina_ustrbuf_append_length(buf, tmp + cur1->pos, cur2->pos - cur1->pos);
12519      }
12520    else
12521      {
12522         const Eina_Unicode *tmp;
12523         tmp = eina_ustrbuf_string_get(n1->unicode);
12524         eina_ustrbuf_append(buf, tmp + cur1->pos);
12525         n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
12526         while (n1 != n2)
12527           {
12528              tmp = eina_ustrbuf_string_get(n1->unicode);
12529              eina_ustrbuf_append_length(buf, tmp,
12530                    eina_ustrbuf_length_get(n1->unicode));
12531              n1 = _NODE_TEXT(EINA_INLIST_GET(n1)->next);
12532           }
12533         tmp = eina_ustrbuf_string_get(n2->unicode);
12534         eina_ustrbuf_append_length(buf, tmp, cur2->pos);
12535      }
12536 
12537    /* Free and return */
12538      {
12539         char *ret;
12540         ret = eina_unicode_unicode_to_utf8(eina_ustrbuf_string_get(buf), NULL);
12541         eina_ustrbuf_free(buf);
12542         return ret;
12543      }
12544 }
12545 
12546 EAPI Eina_List *
evas_textblock_cursor_range_formats_get(const Efl_Text_Cursor_Handle * cur1,const Evas_Textblock_Cursor * cur2)12547 evas_textblock_cursor_range_formats_get(const Efl_Text_Cursor_Handle *cur1, const Evas_Textblock_Cursor *cur2)
12548 {
12549    Evas_Object *eo_obj;
12550    Eina_List *ret = NULL;
12551    Evas_Object_Textblock_Node_Text *n1, *n2;
12552    Evas_Object_Textblock_Node_Format *first, *last;
12553    if (!cur1 || !cur1->node) return NULL;
12554    if (!cur2 || !cur2->node) return NULL;
12555    if (cur1->obj != cur2->obj) return NULL;
12556 
12557    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur1->obj, EFL_CANVAS_OBJECT_CLASS);
12558    evas_object_async_block(obj);
12559    eo_obj = cur1->obj;
12560    TB_HEAD_RETURN(NULL);
12561 
12562    if (evas_textblock_cursor_compare(cur1, cur2) > 0)
12563      {
12564         const Efl_Text_Cursor_Handle *tc;
12565 
12566         tc = cur1;
12567         cur1 = cur2;
12568         cur2 = tc;
12569      }
12570    n1 = cur1->node;
12571    n2 = cur2->node;
12572 
12573    /* FIXME: Change first and last getting to format_before_or_at_pos_get */
12574 
12575    last = n2->format_node;
12576 
12577    /* If n2->format_node is NULL, we don't have formats in the tb/range. */
12578    if (!last)
12579       return NULL;
12580    /* If the found format is on our text node, we should go to the last
12581     * one, otherwise, the one we found is good enough. */
12582    if (last->text_node == n2)
12583      {
12584         Evas_Object_Textblock_Node_Format *fnode = last;
12585         while (fnode && (fnode->text_node == n2))
12586           {
12587              last = fnode;
12588              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
12589           }
12590      }
12591 
12592    /* If the first format node is within the range (i.e points to n1) or if
12593     * we have other formats in the range, go through them */
12594    first = n1->format_node;
12595    if ((first->text_node == n1) || (first != last))
12596      {
12597         Evas_Object_Textblock_Node_Format *fnode = first;
12598         /* Go to the first one in the range */
12599         if (fnode->text_node != n1)
12600           {
12601              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
12602           }
12603 
12604         while (fnode)
12605           {
12606              ret = eina_list_append(ret, fnode);
12607              if (fnode == last)
12608                 break;
12609              fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
12610           }
12611      }
12612 
12613    return ret;
12614 
12615 }
12616 
12617 static char *
_evas_textblock_cursor_range_text_get(const Efl_Text_Cursor_Handle * cur1,const Efl_Text_Cursor_Handle * cur2,Evas_Textblock_Text_Type format)12618 _evas_textblock_cursor_range_text_get(const Efl_Text_Cursor_Handle *cur1, const Efl_Text_Cursor_Handle *cur2, Evas_Textblock_Text_Type format)
12619 {
12620    if (!cur1 || !cur1->node) return NULL;
12621    if (!cur2 || !cur2->node) return NULL;
12622    if (cur1->obj != cur2->obj) return NULL;
12623 
12624    Evas_Object_Protected_Data *obj;
12625 
12626    obj = efl_data_scope_get(cur1->obj, EFL_CANVAS_OBJECT_CLASS);
12627    evas_object_async_block(obj);
12628    if (format == EVAS_TEXTBLOCK_TEXT_MARKUP)
12629       return _evas_textblock_cursor_range_text_markup_get(cur1, cur2);
12630    else if (format == EVAS_TEXTBLOCK_TEXT_PLAIN)
12631       return _evas_textblock_cursor_range_text_plain_get(cur1, cur2);
12632    else
12633       return NULL; /* Not yet supported */
12634 
12635 }
12636 
12637 // Add to legacy api
12638 EAPI char *
evas_textblock_cursor_range_text_get(const Efl_Text_Cursor_Handle * cur1,const Evas_Textblock_Cursor * cur2,Evas_Textblock_Text_Type format)12639 evas_textblock_cursor_range_text_get(const Efl_Text_Cursor_Handle *cur1, const Evas_Textblock_Cursor *cur2, Evas_Textblock_Text_Type format)
12640 {
12641    return _evas_textblock_cursor_range_text_get(cur1, cur2, format);
12642 }
12643 
12644 EAPI const char *
evas_textblock_cursor_paragraph_text_get(const Evas_Textblock_Cursor * cur)12645 evas_textblock_cursor_paragraph_text_get(const Evas_Textblock_Cursor *cur)
12646 {
12647    Efl_Text_Cursor_Handle cur1, cur2;
12648    if (!cur) return NULL;
12649    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
12650    evas_object_async_block(obj);
12651    TB_NULL_CHECK(cur->node, NULL);
12652    if (cur->node->utf8)
12653      {
12654         free(cur->node->utf8);
12655      }
12656    _evas_textblock_cursor_init(&cur1, cur->obj);
12657    _evas_textblock_cursor_init(&cur2, cur->obj);
12658    cur1.node = cur2.node = cur->node;
12659    evas_textblock_cursor_paragraph_char_first(&cur1);
12660    evas_textblock_cursor_paragraph_char_last(&cur2);
12661 
12662    cur->node->utf8 = _evas_textblock_cursor_range_text_get(&cur1, &cur2,
12663          EVAS_TEXTBLOCK_TEXT_MARKUP);
12664    return cur->node->utf8;
12665 }
12666 
12667 EAPI int
evas_textblock_cursor_paragraph_text_length_get(const Evas_Textblock_Cursor * cur)12668 evas_textblock_cursor_paragraph_text_length_get(const Evas_Textblock_Cursor *cur)
12669 {
12670    int len;
12671    if (!cur) return -1;
12672    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
12673    evas_object_async_block(obj);
12674    TB_NULL_CHECK(cur->node, -1);
12675    len = eina_ustrbuf_length_get(cur->node->unicode);
12676 
12677    if (EINA_INLIST_GET(cur->node)->next)
12678       return len - 1; /* Remove the paragraph separator */
12679    else
12680       return len;
12681 }
12682 
12683 EAPI const Evas_Object_Textblock_Node_Format *
evas_textblock_cursor_format_get(const Evas_Textblock_Cursor * cur)12684 evas_textblock_cursor_format_get(const Evas_Textblock_Cursor *cur)
12685 {
12686    if (!cur) return NULL;
12687    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
12688    evas_object_async_block(obj);
12689    TB_NULL_CHECK(cur->node, NULL);
12690    return _evas_textblock_cursor_node_format_at_pos_get(cur);
12691 }
12692 
12693 EAPI const char *
evas_textblock_node_format_text_get(const Evas_Object_Textblock_Node_Format * fmt)12694 evas_textblock_node_format_text_get(const Evas_Object_Textblock_Node_Format *fmt)
12695 {
12696    static char *ret = NULL;
12697    char *tmp;
12698    const char *stripped;
12699    size_t stripped_len;
12700 
12701    if (!fmt) return NULL;
12702 
12703    if (ret) free(ret);
12704    stripped = fmt->orig_format;
12705    stripped_len = strlen(fmt->orig_format);
12706    if (stripped[stripped_len - 1] == '/')
12707      {
12708         stripped_len--;
12709      }
12710    else if (stripped[0] == '/')
12711      {
12712         stripped++;
12713         stripped_len--;
12714      }
12715 
12716    ret = calloc(stripped_len + 2 + 1, sizeof(char));
12717    tmp = ret;
12718 
12719    if (fmt->opener && !fmt->own_closer)
12720      {
12721         *(tmp++) = '+';
12722         *(tmp++) = ' ';
12723      }
12724    else if (!fmt->opener)
12725      {
12726         *(tmp++) = '-';
12727         *(tmp++) = ' ';
12728      }
12729    strncpy(tmp, stripped, stripped_len);
12730    return ret;
12731 }
12732 
12733 static void
_evas_textblock_cursor_at_format_set(Efl_Text_Cursor_Handle * cur,const Evas_Object_Textblock_Node_Format * fmt)12734 _evas_textblock_cursor_at_format_set(Efl_Text_Cursor_Handle *cur,
12735       const Evas_Object_Textblock_Node_Format *fmt)
12736 {
12737    if (!fmt || !cur) return;
12738    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
12739    evas_object_async_block(obj);
12740    cur->node = fmt->text_node;
12741    cur->pos = _evas_textblock_node_format_pos_get(fmt);
12742 }
12743 
12744 EAPI void
evas_textblock_cursor_at_format_set(Evas_Textblock_Cursor * cur,const Evas_Object_Textblock_Node_Format * fmt)12745 evas_textblock_cursor_at_format_set(Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Format *fmt)
12746 {
12747    _evas_textblock_cursor_at_format_set(cur, fmt);
12748 }
12749 
12750 static Eina_Bool
_evas_textblock_cursor_format_is_visible_get(const Efl_Text_Cursor_Handle * cur)12751 _evas_textblock_cursor_format_is_visible_get(const Efl_Text_Cursor_Handle *cur)
12752 {
12753    const Eina_Unicode *text;
12754 
12755    if (!cur) return EINA_FALSE;
12756    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
12757    evas_object_async_block(obj);
12758    TB_NULL_CHECK(cur->node, EINA_FALSE);
12759    if (!_evas_textblock_cursor_is_format(cur)) return EINA_FALSE;
12760    text = eina_ustrbuf_string_get(cur->node->unicode);
12761    return EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(text[cur->pos]);
12762 }
12763 
12764 EAPI Eina_Bool
evas_textblock_cursor_format_is_visible_get(const Evas_Textblock_Cursor * cur)12765 evas_textblock_cursor_format_is_visible_get(const Evas_Textblock_Cursor *cur)
12766 {
12767    return _evas_textblock_cursor_format_is_visible_get(cur);
12768 }
12769 
12770 #ifdef BIDI_SUPPORT
12771 static Evas_Object_Textblock_Line*
_find_layout_line_by_item(Evas_Object_Textblock_Paragraph * par,Evas_Object_Textblock_Item * _it)12772 _find_layout_line_by_item(Evas_Object_Textblock_Paragraph *par, Evas_Object_Textblock_Item *_it)
12773 {
12774    Evas_Object_Textblock_Line *ln;
12775 
12776    EINA_INLIST_FOREACH(par->lines, ln)
12777      {
12778         Evas_Object_Textblock_Item *it;
12779 
12780         EINA_INLIST_FOREACH(ln->items, it)
12781           {
12782              if (_it == it)
12783                return ln;
12784           }
12785      }
12786    return NULL;
12787 }
12788 #endif
12789 
12790 EAPI Eina_Bool
evas_textblock_cursor_geometry_bidi_get(const Efl_Text_Cursor_Handle * cur,Evas_Coord * cx,Evas_Coord * cy,Evas_Coord * cw,Evas_Coord * ch,Evas_Coord * cx2,Evas_Coord * cy2,Evas_Coord * cw2,Evas_Coord * ch2,Evas_Textblock_Cursor_Type ctype)12791 evas_textblock_cursor_geometry_bidi_get(const Efl_Text_Cursor_Handle *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch, Evas_Coord *cx2, Evas_Coord *cy2, Evas_Coord *cw2, Evas_Coord *ch2, Evas_Textblock_Cursor_Type ctype)
12792 {
12793    if (!cur) return EINA_FALSE;
12794    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
12795    ASYNC_BLOCK;
12796    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
12797 
12798    evas_object_async_block(obj);
12799 
12800    if (!_relayout_if_needed(cur->obj, o)) return EINA_FALSE;
12801 
12802    if (ctype == EVAS_TEXTBLOCK_CURSOR_UNDER)
12803      {
12804         evas_textblock_cursor_pen_geometry_get(cur, cx, cy, cw, ch);
12805         return EINA_FALSE;
12806      }
12807 
12808 #ifdef BIDI_SUPPORT
12809 #define IS_RTL(par) ((par) % 2)
12810 #define IS_DIFFERENT_DIR(l1, l2) (IS_RTL(l1) != IS_RTL(l2))
12811    else
12812      {
12813         Evas_Object_Textblock_Line *ln = NULL;
12814         Evas_Object_Textblock_Item *it = NULL;
12815         _find_layout_item_match(cur, &ln, &it);
12816         if (ln && it)
12817           {
12818              if (ln->par->is_bidi)
12819                {
12820                   if (cw) *cw = 0;
12821                   if (cw2) *cw2 = 0;
12822 
12823                   /* If we are at the start or the end of the item there's a chance
12824                    * we'll want a split cursor.  */
12825                   Evas_Object_Textblock_Item *previt = NULL;
12826                   Evas_Object_Textblock_Item *it1 = NULL, *it2 = NULL;
12827                   Evas_Coord adv1 = 0, adv2 = 0;
12828 
12829                   if (cur->pos == it->text_pos)
12830                     {
12831                        EvasBiDiLevel par_level, it_level, previt_level;
12832 
12833                        _layout_update_bidi_props(o, ln->par);
12834                        par_level = *(ln->par->bidi_props->embedding_levels);
12835                        it_level = ln->par->bidi_props->embedding_levels[it->text_pos];
12836                        /* Get the logically previous item. */
12837                          {
12838                             Eina_List *itr;
12839                             Evas_Object_Textblock_Item *ititr;
12840 
12841                             EINA_LIST_FOREACH(ln->par->logical_items, itr, ititr)
12842                               {
12843                                  if (ititr == it)
12844                                    break;
12845                                  previt = ititr;
12846                               }
12847 
12848                             if (previt)
12849                               {
12850                                  previt_level = ln->par->bidi_props->embedding_levels[previt->text_pos];
12851                               }
12852                          }
12853 
12854                        if (previt && (it_level != previt_level))
12855                          {
12856                             Evas_Object_Textblock_Item *curit = NULL, *curit_opp = NULL;
12857                             EvasBiDiLevel cur_level;
12858 
12859                             if (it_level > previt_level)
12860                               {
12861                                  curit = it;
12862                                  curit_opp = previt;
12863                                  cur_level = it_level;
12864                               }
12865                             else
12866                               {
12867                                  curit = previt;
12868                                  curit_opp = it;
12869                                  cur_level = previt_level;
12870                               }
12871 
12872                             if (((curit == it) && (!IS_RTL(par_level))) ||
12873                                 ((curit == previt) && (IS_RTL(par_level))))
12874                               {
12875                                  adv1 = (IS_DIFFERENT_DIR(cur_level, par_level)) ?
12876                                                           curit_opp->adv : 0;
12877                                  adv2 = curit->adv;
12878                               }
12879                             else if (((curit == previt) && (!IS_RTL(par_level))) ||
12880                                      ((curit == it) && (IS_RTL(par_level))))
12881                               {
12882                                  adv1 = (IS_DIFFERENT_DIR(cur_level, par_level)) ?
12883                                                           0 : curit->adv;
12884                                  adv2 = 0;
12885                               }
12886 
12887                             if (!IS_DIFFERENT_DIR(cur_level, par_level))
12888                               curit_opp = curit;
12889 
12890                             it1 = curit_opp;
12891                             it2 = curit;
12892                          }
12893                        /* Clear the bidi props because we don't need them anymore. */
12894                        evas_bidi_paragraph_props_unref(ln->par->bidi_props);
12895                        ln->par->bidi_props = NULL;
12896                     }
12897                   /* Handling last char in line (or in paragraph).
12898                    * T.e. prev condition didn't work, so we are not standing in the beginning of item,
12899                    * but in the end of line or paragraph. */
12900                   else if (evas_textblock_cursor_eol_get(cur))
12901                     {
12902                        EvasBiDiLevel par_level, it_level;
12903 
12904                        _layout_update_bidi_props(o, ln->par);
12905                        par_level = *(ln->par->bidi_props->embedding_levels);
12906                        it_level = ln->par->bidi_props->embedding_levels[it->text_pos];
12907 
12908                        if (it_level > par_level)
12909                          {
12910                             Evas_Object_Textblock_Item *lastit = it;
12911 
12912                             if (IS_RTL(par_level)) /* RTL par*/
12913                               {
12914                                  /*  We know, that all the items before current are of the same or bigger embedding level.
12915                                   *  So search backwards for the first one. */
12916                                  while (EINA_INLIST_GET(lastit)->prev)
12917                                    {
12918                                       lastit = _EINA_INLIST_CONTAINER(it, EINA_INLIST_GET(lastit)->prev);
12919                                    }
12920 
12921                                  adv1 = 0;
12922                                  adv2 = it->adv;
12923                               }
12924                             else /* LTR par */
12925                               {
12926                                  /*  We know, that all the items after current are of bigger or same embedding level.
12927                                   *  So search forward for the last one. */
12928                                  while (EINA_INLIST_GET(lastit)->next)
12929                                    {
12930                                       lastit = _EINA_INLIST_CONTAINER(it, EINA_INLIST_GET(lastit)->next);
12931                                    }
12932 
12933                                  adv1 = lastit->adv;
12934                                  adv2 = 0;
12935                               }
12936 
12937                             it1 = lastit;
12938                             it2 = it;
12939                          }
12940                        /* Clear the bidi props because we don't need them anymore. */
12941                        evas_bidi_paragraph_props_unref(ln->par->bidi_props);
12942                        ln->par->bidi_props = NULL;
12943                     }
12944 
12945                   if (it1 && it2)
12946                     {
12947                        Evas_Object_Textblock_Line *ln1 = NULL, *ln2 = NULL;
12948                        ln1 = _find_layout_line_by_item(ln->par, it1);
12949                        if (cx) *cx = ln1->x + it1->x + adv1;
12950                        if (cy) *cy = ln1->par->y + ln1->y;
12951                        if (ch) *ch = ln1->h;
12952 
12953                        ln2 = _find_layout_line_by_item(ln->par, it2);
12954                        if (cx2) *cx2 = ln2->x + it2->x + adv2;
12955                        if (cy2) *cy2 = ln2->par->y + ln2->y;
12956                        if (ch2) *ch2 = ln2->h;
12957 
12958                        return EINA_TRUE;
12959                     }
12960                }
12961           }
12962      }
12963 #undef IS_DIFFERENT_DIR
12964 #undef IS_RTL
12965 #else
12966    (void) cx2;
12967    (void) cy2;
12968    (void) cw2;
12969    (void) ch2;
12970 #endif
12971    evas_textblock_cursor_geometry_get(cur, cx, cy, cw, ch, NULL,ctype);
12972    return EINA_FALSE;
12973 }
12974 
12975 EAPI int
evas_textblock_cursor_geometry_get(const Efl_Text_Cursor_Handle * cur,Evas_Coord * cx,Evas_Coord * cy,Evas_Coord * cw,Evas_Coord * ch,Evas_BiDi_Direction * dir,Evas_Textblock_Cursor_Type ctype)12976 evas_textblock_cursor_geometry_get(const Efl_Text_Cursor_Handle *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch, Evas_BiDi_Direction *dir, Evas_Textblock_Cursor_Type ctype)
12977 {
12978    int ret = -1;
12979    if (!cur) return -1;
12980    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
12981    evas_object_async_block(obj);
12982    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
12983 
12984    _relayout_if_needed(cur->obj, o);
12985 
12986    if (ctype == EVAS_TEXTBLOCK_CURSOR_UNDER)
12987      {
12988         Evas_Object_Textblock_Line *ln;
12989         Evas_Object_Textblock_Item *it;
12990 
12991         ret = evas_textblock_cursor_pen_geometry_get(cur, cx, cy, cw, ch);
12992         _find_layout_item_match(cur, &ln, &it);
12993         if (ret >= 0)
12994           {
12995              Evas_BiDi_Direction itdir =
12996                 (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
12997                 _ITEM_TEXT(it)->text_props.bidi_dir :
12998                 _ITEM_FORMAT(it)->bidi_dir;
12999              if (dir) *dir = itdir;
13000           }
13001      }
13002    else if (ctype == EVAS_TEXTBLOCK_CURSOR_BEFORE)
13003      {
13004         /* In the case of a "before cursor", we should get the coordinates
13005          * of just after the previous char (which in bidi text may not be
13006          * just before the current char). */
13007         Evas_Coord x, y, w, h;
13008         Evas_Object_Textblock_Line *ln;
13009         Evas_Object_Textblock_Item *it;
13010 
13011         ret = evas_textblock_cursor_pen_geometry_get(cur, &x, &y, &w, &h);
13012         _find_layout_item_match(cur, &ln, &it);
13013         if (ret >= 0)
13014           {
13015              Evas_BiDi_Direction itdir =
13016                 (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
13017                 _ITEM_TEXT(it)->text_props.bidi_dir :
13018                 _ITEM_FORMAT(it)->bidi_dir;
13019              if (itdir == EVAS_BIDI_DIRECTION_RTL)
13020                {
13021                   if (cx) *cx = x + w;
13022                }
13023              else
13024                {
13025                   if (cx) *cx = x;
13026                }
13027              if (cy) *cy = y;
13028              if (cw) *cw = 0;
13029              if (ch) *ch = h;
13030              if (dir) *dir = itdir;
13031           }
13032      }
13033    return ret;
13034 }
13035 
13036 /**
13037  * @internal
13038  * Returns the geometry/pen position (depending on query_func) of the char
13039  * at pos.
13040  *
13041  * @param cur the position of the char.
13042  * @param query_func the query function to use.
13043  * @param cx the x of the char (or pen_x in the case of pen position).
13044  * @param cy the y of the char.
13045  * @param cw the w of the char (or advance in the case pen position).
13046  * @param ch the h of the char.
13047  * @return line number of the char on success, -1 on error.
13048  */
13049 static int
_evas_textblock_cursor_char_pen_geometry_common_get(int (* query_func)(void * data,Evas_Font_Set * font,const Evas_Text_Props * intl_props,int pos,int * cx,int * cy,int * cw,int * ch),const Evas_Textblock_Cursor * cur,Evas_Coord * cx,Evas_Coord * cy,Evas_Coord * cw,Evas_Coord * ch)13050 _evas_textblock_cursor_char_pen_geometry_common_get(int (*query_func) (void *data, Evas_Font_Set *font, const Evas_Text_Props *intl_props, int pos, int *cx, int *cy, int *cw, int *ch), const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
13051 {
13052    Evas_Object_Textblock_Line *ln = NULL;
13053    Evas_Object_Textblock_Item *it = NULL;
13054    Evas_Object_Textblock_Text_Item *ti = NULL;
13055    Evas_Object_Textblock_Format_Item *fi = NULL;
13056    int x = 0, y = 0, w = 0, h = 0;
13057    int pos;
13058    Eina_Bool previous_format;
13059 
13060    if (!cur) return -1;
13061    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
13062 
13063    _relayout_if_needed(cur->obj, o);
13064 
13065    if (!cur->node)
13066      {
13067         if (!o->text_nodes)
13068           {
13069              if (!o->paragraphs) return -1;
13070              ln = o->paragraphs->lines;
13071              if (!ln) return -1;
13072              if (cx) *cx = ln->x;
13073              if (cy) *cy = ln->par->y + ln->y;
13074              if (cw) *cw = ln->w;
13075              if (ch) *ch = ln->h;
13076              return ln->par->line_no + ln->line_no;
13077           }
13078         else
13079           return -1;
13080      }
13081 
13082    previous_format = _find_layout_item_match(cur, &ln, &it);
13083    if (!it)
13084      {
13085         return -1;
13086      }
13087    if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
13088      {
13089         ti = _ITEM_TEXT(it);
13090      }
13091    else
13092      {
13093         fi = _ITEM_FORMAT(it);
13094      }
13095 
13096    if (ln && ti)
13097      {
13098         pos = cur->pos - ti->parent.text_pos;
13099 
13100         if (pos < 0) pos = 0;
13101         if (ti->parent.format->font.font)
13102           {
13103              Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
13104              query_func(ENC,
13105                    ti->parent.format->font.font,
13106                    &ti->text_props,
13107                    pos,
13108                    &x, &y, &w, &h);
13109           }
13110 
13111         x += ln->x + _ITEM(ti)->x;
13112 
13113         if (x < ln->x)
13114           {
13115              x = ln->x;
13116           }
13117         y = ln->par->y + ln->y;
13118         h = ln->h;
13119      }
13120    else if (ln && fi)
13121      {
13122         if (previous_format)
13123           {
13124              if (_IS_LINE_SEPARATOR(fi->item))
13125                {
13126                   x = 0;
13127                   y = ln->par->y + ln->y + ln->h;
13128                }
13129              else
13130                {
13131 #ifdef BIDI_SUPPORT
13132                   if (ln->par->direction == EVAS_BIDI_DIRECTION_RTL)
13133                     {
13134                        x = ln->x;
13135                     }
13136                   else
13137 #endif
13138                     {
13139                        x = ln->x + ln->w;
13140                     }
13141                   y = ln->par->y + ln->y;
13142                }
13143              w = 0;
13144              h = ln->h;
13145           }
13146         else
13147           {
13148              x = ln->x + _ITEM(fi)->x;
13149              y = ln->par->y + ln->y;
13150              w = _ITEM(fi)->w;
13151              h = ln->h;
13152           }
13153      }
13154    else
13155      {
13156         return -1;
13157      }
13158    if (cx) *cx = x;
13159    if (cy) *cy = y;
13160    if (cw) *cw = w;
13161    if (ch) *ch = h;
13162    return ln->par->line_no + ln->line_no;
13163 }
13164 
13165 EAPI int
evas_textblock_cursor_char_geometry_get(const Evas_Textblock_Cursor * cur,Evas_Coord * cx,Evas_Coord * cy,Evas_Coord * cw,Evas_Coord * ch)13166 evas_textblock_cursor_char_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
13167 {
13168    if (!cur) return -1;
13169    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
13170    evas_object_async_block(obj);
13171    return _evas_textblock_cursor_char_pen_geometry_common_get(
13172          ENFN->font_char_coords_get, cur, cx, cy, cw, ch);
13173 }
13174 
13175 EAPI int
evas_textblock_cursor_pen_geometry_get(const Evas_Textblock_Cursor * cur,Evas_Coord * cx,Evas_Coord * cy,Evas_Coord * cw,Evas_Coord * ch)13176 evas_textblock_cursor_pen_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
13177 {
13178    if (!cur) return -1;
13179    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
13180    evas_object_async_block(obj);
13181    return _evas_textblock_cursor_char_pen_geometry_common_get(
13182          ENFN->font_pen_coords_get, cur, cx, cy, cw, ch);
13183 }
13184 
13185 EAPI int
evas_textblock_cursor_line_geometry_get(const Evas_Textblock_Cursor * cur,Evas_Coord * cx,Evas_Coord * cy,Evas_Coord * cw,Evas_Coord * ch)13186 evas_textblock_cursor_line_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
13187 {
13188    Evas_Object_Textblock_Line *ln = NULL;
13189    Evas_Object_Textblock_Item *it = NULL;
13190    int x, y, w, h;
13191 
13192    if (!cur) return -1;
13193    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
13194    evas_object_async_block(obj);
13195    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
13196 
13197    _relayout_if_needed(cur->obj, o);
13198 
13199    if (!cur->node)
13200      {
13201         ln = o->paragraphs->lines;
13202      }
13203    else
13204      {
13205         _find_layout_item_match(cur, &ln, &it);
13206      }
13207    if (!ln) return -1;
13208    x = ln->x;
13209    y = ln->par->y + ln->y;
13210    w = ln->w;
13211    h = ln->h;
13212    if (cx) *cx = x;
13213    if (cy) *cy = y;
13214    if (cw) *cw = w;
13215    if (ch) *ch = h;
13216    return ln->par->line_no + ln->line_no;
13217 }
13218 
13219 EAPI Eina_Bool
evas_textblock_cursor_visible_range_get(Efl_Text_Cursor_Handle * start,Evas_Textblock_Cursor * end)13220 evas_textblock_cursor_visible_range_get(Efl_Text_Cursor_Handle *start, Evas_Textblock_Cursor *end)
13221 {
13222    Eo * eo_obj = start->obj;
13223    Evas *eo_e;
13224    Evas_Coord cy, ch;
13225    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
13226    evas_object_async_block(obj);
13227    TB_HEAD_RETURN(EINA_FALSE);
13228    eo_e = evas_object_evas_get(eo_obj);
13229    Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
13230    cy = 0 - obj->cur->geometry.y;
13231    ch = e->viewport.h;
13232    evas_textblock_cursor_line_coord_set(start, cy);
13233    evas_textblock_cursor_line_coord_set(end, cy + ch);
13234    evas_textblock_cursor_line_char_last(end);
13235 
13236    return EINA_TRUE;
13237 }
13238 
13239 EOLIAN static Eina_Bool
_efl_canvas_textblock_visible_range_get(Eo * eo_obj EINA_UNUSED,Efl_Canvas_Textblock_Data * pd EINA_UNUSED,Efl_Text_Cursor_Object * start,Efl_Text_Cursor_Object * end)13240 _efl_canvas_textblock_visible_range_get(Eo *eo_obj EINA_UNUSED,
13241       Efl_Canvas_Textblock_Data *pd EINA_UNUSED,
13242       Efl_Text_Cursor_Object *start, Efl_Text_Cursor_Object *end)
13243 {
13244    return evas_textblock_cursor_visible_range_get(
13245                         efl_text_cursor_object_handle_get(start),
13246                         efl_text_cursor_object_handle_get(end)
13247                         );
13248 }
13249 
13250 static Eina_Bool
_evas_textblock_cursor_coord_set(Evas_Textblock_Cursor * cur,Evas_Coord x,Evas_Coord y,Eina_Bool per_cluster)13251 _evas_textblock_cursor_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord x, Evas_Coord y, Eina_Bool per_cluster)
13252 {
13253    Evas_Object_Textblock_Paragraph *found_par;
13254    Evas_Object_Textblock_Line *ln;
13255    Evas_Object_Textblock_Item *it = NULL;
13256    Eina_Bool ret = EINA_FALSE;
13257 
13258    if (!cur) return ret;
13259 
13260    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
13261    evas_object_async_block(obj);
13262    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
13263 
13264    _relayout_if_needed(cur->obj, o);
13265 
13266    x += o->style_pad.l;
13267    y += o->style_pad.t;
13268 
13269    found_par = _layout_find_paragraph_by_y(o, y);
13270    if (found_par)
13271      {
13272         _layout_paragraph_render(o, found_par);
13273         EINA_INLIST_FOREACH(found_par->lines, ln)
13274           {
13275              if (ln->par->y + ln->y > y) break;
13276              if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
13277                {
13278                   /* If before or after the line, go to start/end according
13279                    * to paragraph direction. */
13280                   if (x < ln->x)
13281                     {
13282                        cur->pos = ln->items->text_pos;
13283                        cur->node = found_par->text_node;
13284                        if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
13285                          {
13286                             evas_textblock_cursor_line_char_last(cur);
13287                          }
13288                        else
13289                          {
13290                             evas_textblock_cursor_line_char_first(cur);
13291                          }
13292                        ret = EINA_TRUE;
13293                        goto end;
13294                     }
13295                   else if (x >= ln->x + ln->w)
13296                     {
13297                        cur->pos = ln->items->text_pos;
13298                        cur->node = found_par->text_node;
13299                        if (found_par->direction == EVAS_BIDI_DIRECTION_RTL)
13300                          {
13301                             evas_textblock_cursor_line_char_first(cur);
13302                          }
13303                        else
13304                          {
13305                             evas_textblock_cursor_line_char_last(cur);
13306                          }
13307                        ret = EINA_TRUE;
13308                        goto end;
13309                     }
13310 
13311                   EINA_INLIST_FOREACH(ln->items, it)
13312                     {
13313                        if (((it->x + ln->x) <= x) && (((it->x + ln->x) + it->adv) > x))
13314                          {
13315                             if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
13316                               {
13317                                  int pos;
13318                                  int cx, cy, cw, ch;
13319                                  Evas_Object_Textblock_Text_Item *ti;
13320                                  ti = _ITEM_TEXT(it);
13321 
13322                                  pos = -1;
13323                                  if (ti->parent.format->font.font)
13324                                    pos = ENFN->font_char_at_coords_get(
13325                                          ENC,
13326                                          ti->parent.format->font.font,
13327                                          &ti->text_props,
13328                                          x - it->x - ln->x, 0,
13329                                          &cx, &cy, &cw, &ch);
13330                                  if (pos < 0)
13331                                    goto end;
13332 
13333                                  if ((pos > 0) && per_cluster)
13334                                    {
13335                                       size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
13336                                       char *grapheme_breaks = _evas_textblock_grapheme_breaks_new(it, len);
13337 
13338                                       /* If current position is not breakable,
13339                                        * try to move cursor to a nearest breakable position. */
13340                                       if (grapheme_breaks && (grapheme_breaks[pos + it->text_pos - 1] != GRAPHEMEBREAK_BREAK))
13341                                         {
13342                                            int left_index = pos + it->text_pos - 1;
13343                                            size_t right_index = pos + it->text_pos - 1;
13344                                            int temp_index;
13345                                            int lx, rx;
13346 
13347                                            /* To the left */
13348                                            while ((left_index >= 0) &&
13349                                                   (grapheme_breaks[left_index] != GRAPHEMEBREAK_BREAK))
13350                                              {
13351                                                 left_index--;
13352                                              }
13353 
13354                                            temp_index = left_index - it->text_pos + 1;
13355                                            if (temp_index < 0)
13356                                              temp_index = 0;
13357 
13358                                            ENFN->font_pen_coords_get(ENC,
13359                                                                      ti->parent.format->font.font,
13360                                                                      &ti->text_props,
13361                                                                      temp_index,
13362                                                                      &lx, NULL, NULL, NULL);
13363 
13364                                            /* To the right */
13365                                            while ((right_index < len) &&
13366                                                   (grapheme_breaks[right_index] != GRAPHEMEBREAK_BREAK))
13367                                              {
13368                                                 right_index++;
13369                                              }
13370 
13371                                            ENFN->font_pen_coords_get(ENC,
13372                                                                      ti->parent.format->font.font,
13373                                                                      &ti->text_props,
13374                                                                      right_index - it->text_pos + 1,
13375                                                                      &rx, NULL, NULL, NULL);
13376 
13377                                            /* Decide a nearest position by checking its geometry. */
13378                                            if (((ti->text_props.bidi_dir != EVAS_BIDI_DIRECTION_RTL) &&
13379                                                 ((ln->x + it->x + rx - x) >= (x - (lx + ln->x + it->x)))) ||
13380                                                ((ti->text_props.bidi_dir == EVAS_BIDI_DIRECTION_RTL) &&
13381                                                 ((ln->x + it->x + lx - x) >= (x - (rx + ln->x + it->x)))))
13382                                              {
13383                                                 pos = left_index - it->text_pos + 1;
13384                                              }
13385                                            else
13386                                              {
13387                                                 pos = right_index - it->text_pos + 1;
13388                                              }
13389                                         }
13390 
13391                                       free(grapheme_breaks);
13392                                    }
13393 
13394                                  cur->pos = pos + it->text_pos;
13395                                  cur->node = it->text_node;
13396                                  ret = EINA_TRUE;
13397                                  goto end;
13398                               }
13399                             else
13400                               {
13401                                  Evas_Object_Textblock_Format_Item *fi;
13402                                  fi = _ITEM_FORMAT(it);
13403                                  /* Lets keep cur position half way for easy positioning */
13404                                  if (x > (ln->x + it->x + (it->adv / 2)))
13405                                    {
13406                                       cur->pos = fi->parent.text_pos + 1;
13407                                    }
13408                                  else
13409                                    {
13410                                       cur->pos = fi->parent.text_pos;
13411                                    }
13412                                  cur->node = found_par->text_node;
13413                                  ret = EINA_TRUE;
13414                                  goto end;
13415                               }
13416                          }
13417                     }
13418                }
13419           }
13420      }
13421 
13422    if (o->paragraphs)
13423      {
13424         Evas_Object_Textblock_Line *first_line = o->paragraphs->lines;
13425         if (y >= o->paragraphs->y + o->formatted.h + o->style_pad.t + o->style_pad.b)
13426           {
13427              /* If we are after the last paragraph, use the last position in the
13428               * text. */
13429              evas_textblock_cursor_paragraph_last(cur);
13430              ret = EINA_TRUE;
13431              goto end;
13432           }
13433         else if (o->paragraphs && (y < (o->paragraphs->y + first_line->y)))
13434           {
13435              evas_textblock_cursor_paragraph_first(cur);
13436              ret = EINA_TRUE;
13437              goto end;
13438           }
13439      }
13440 
13441 end:
13442    if (ret)
13443      {
13444         _evas_textblock_cursor_object_changed(cur);
13445      }
13446    return ret;
13447 }
13448 
13449 EAPI Eina_Bool
evas_textblock_cursor_char_coord_set(Evas_Textblock_Cursor * cur,Evas_Coord x,Evas_Coord y)13450 evas_textblock_cursor_char_coord_set(Evas_Textblock_Cursor *cur,  Evas_Coord x, Evas_Coord y)
13451 {
13452    return _evas_textblock_cursor_coord_set(cur, x, y, EINA_FALSE);
13453 }
13454 
13455 EAPI Eina_Bool
evas_textblock_cursor_cluster_coord_set(Evas_Textblock_Cursor * cur,Evas_Coord x,Evas_Coord y)13456 evas_textblock_cursor_cluster_coord_set(Evas_Textblock_Cursor *cur,  Evas_Coord x, Evas_Coord y)
13457 {
13458    return _evas_textblock_cursor_coord_set(cur, x, y, EINA_TRUE);
13459 }
13460 
13461 EAPI int
evas_textblock_cursor_line_coord_set(Evas_Textblock_Cursor * cur,Evas_Coord y)13462 evas_textblock_cursor_line_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord y)
13463 {
13464    Evas_Object_Textblock_Paragraph *found_par;
13465    Evas_Object_Textblock_Line *ln;
13466 
13467    if (!cur) return -1;
13468    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
13469    evas_object_async_block(obj);
13470    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
13471 
13472    _relayout_if_needed(cur->obj, o);
13473 
13474    y += o->style_pad.t;
13475 
13476    found_par = _layout_find_paragraph_by_y(o, y);
13477 
13478    if (found_par)
13479      {
13480         /* If we are before the first paragraph, use the first position in the text */
13481         if ((found_par->y >= y) && (!EINA_INLIST_GET(found_par)->prev))
13482           {
13483              evas_textblock_cursor_paragraph_first(cur);
13484              return 0;
13485           }
13486 
13487         _layout_paragraph_render(o, found_par);
13488         EINA_INLIST_FOREACH(found_par->lines, ln)
13489           {
13490              if (ln->par->y + ln->y > y) break;
13491              if ((ln->par->y + ln->y <= y) && ((ln->par->y + ln->y + ln->h) > y))
13492                {
13493                   evas_textblock_cursor_line_set(cur, ln->par->line_no +
13494                         ln->line_no);
13495                   return ln->par->line_no + ln->line_no;
13496                }
13497           }
13498      }
13499    else if (o->paragraphs && (y >= o->paragraphs->y + o->formatted.h + o->style_pad.t + o->style_pad.b))
13500      {
13501         int line_no = 0;
13502         /* If we are after the last paragraph, use the last position in the
13503          * text. */
13504         evas_textblock_cursor_paragraph_last(cur);
13505         if (cur->node && cur->node->par)
13506           {
13507              line_no = cur->node->par->line_no;
13508              if (cur->node->par->lines)
13509                {
13510                   line_no += ((Evas_Object_Textblock_Line *)
13511                         EINA_INLIST_GET(cur->node->par->lines)->last)->line_no;
13512                }
13513           }
13514         return line_no;
13515      }
13516    else if (o->paragraphs && (y < o->paragraphs->y))
13517      {
13518         int line_no = 0;
13519         evas_textblock_cursor_paragraph_first(cur);
13520         if (cur->node && cur->node->par)
13521           {
13522              line_no = cur->node->par->line_no;
13523           }
13524         return line_no;
13525      }
13526    return -1;
13527 }
13528 
13529 /**
13530  * @internal
13531  * Updates x and w according to the text direction, position in text and
13532  * if it's a special case switch
13533  *
13534  * @param ti the text item we are working on
13535  * @param x the current x (we get) and the x we return
13536  * @param w the current w (we get) and the w we return
13537  * @param start if this is the first item or not
13538  * @param switch_items toogles item switching (rtl cases)
13539  */
13540 static void
_evas_textblock_range_calc_x_w(const Evas_Object_Textblock_Item * it,Evas_Coord * x,Evas_Coord * w,Eina_Bool start,Eina_Bool switch_items)13541 _evas_textblock_range_calc_x_w(const Evas_Object_Textblock_Item *it,
13542       Evas_Coord *x, Evas_Coord *w, Eina_Bool start, Eina_Bool switch_items)
13543 {
13544    if ((start && !switch_items) || (!start && switch_items))
13545      {
13546 #ifdef BIDI_SUPPORT
13547         if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
13548             _ITEM_TEXT(it)->text_props.bidi_dir == EVAS_BIDI_DIRECTION_RTL)
13549             ||
13550             ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
13551              _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
13552           {
13553              *w = *x + *w;
13554              *x = 0;
13555           }
13556         else
13557 #endif
13558           {
13559              *w = it->adv - *x;
13560           }
13561      }
13562    else
13563      {
13564 #ifdef BIDI_SUPPORT
13565         if (((it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
13566             _ITEM_TEXT(it)->text_props.bidi_dir == EVAS_BIDI_DIRECTION_RTL)
13567             ||
13568             ((it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) &&
13569              _ITEM_FORMAT(it)->bidi_dir == EVAS_BIDI_DIRECTION_RTL))
13570           {
13571              *x = *x + *w;
13572              *w = it->adv - *x;
13573           }
13574         else
13575 #endif
13576           {
13577              *w = *x;
13578              *x = 0;
13579           }
13580      }
13581 
13582 }
13583 
13584 /**
13585  * @internal
13586  * Returns the geometry of the range in line ln. Cur1 is the start cursor,
13587  * cur2 is the end cursor, NULL means from the start or to the end accordingly.
13588  * Assumes that ln is valid, and that at least one of cur1 and cur2 is not NULL.
13589  *
13590  * @param ln the line to work on.
13591  * @param cur1 the start cursor
13592  * @param cur2 the end cursor
13593  * @return Returns the geometry of the range
13594  */
13595 static Eina_List *
_evas_textblock_cursor_range_in_line_geometry_get(const Evas_Object_Textblock_Line * ln,const Efl_Text_Cursor_Handle * cur1,const Efl_Text_Cursor_Handle * cur2)13596 _evas_textblock_cursor_range_in_line_geometry_get(
13597       const Evas_Object_Textblock_Line *ln, const Efl_Text_Cursor_Handle *cur1,
13598       const Efl_Text_Cursor_Handle *cur2)
13599 {
13600    Evas_Object_Textblock_Item *it;
13601    Evas_Object_Textblock_Item *it1, *it2;
13602    Eina_List *rects = NULL;
13603    Evas_Textblock_Rectangle *tr;
13604    size_t start, end;
13605    Eina_Bool switch_items;
13606    const Efl_Text_Cursor_Handle *cur;
13607 
13608    cur = (cur1) ? cur1 : cur2;
13609 
13610    if (!cur) return NULL;
13611    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
13612 
13613    /* Find the first and last items */
13614    it1 = it2 = NULL;
13615    start = end = 0;
13616    EINA_INLIST_FOREACH(ln->items, it)
13617      {
13618         size_t item_len;
13619         item_len = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
13620            _ITEM_TEXT(it)->text_props.text_len
13621            : 1;
13622         if ((!cur1 || (cur1->pos < it->text_pos + item_len)) &&
13623               (!cur2 || (cur2->pos > it->text_pos)))
13624           {
13625              if (!it1)
13626                {
13627                   it1 = it;
13628                   start = item_len; /* start stores the first item_len */
13629                }
13630              it2 = it;
13631              end = item_len; /* end stores the last item_len */
13632           }
13633      }
13634 
13635    /* If we couldn't find even one item, return */
13636    if (!it1) return NULL;
13637 
13638    /* If the first item is logically before or equal the second item
13639     * we have to set start and end differently than in the other case */
13640    if (it1->text_pos <= it2->text_pos)
13641      {
13642         start = (cur1) ? (cur1->pos - it1->text_pos) : 0;
13643         end = (cur2) ? (cur2->pos - it2->text_pos) : end;
13644         switch_items = EINA_FALSE;
13645      }
13646    else
13647      {
13648         start = (cur2) ? (cur2->pos - it1->text_pos) : start;
13649         end = (cur1) ? (cur1->pos - it2->text_pos) : 0;
13650         switch_items = EINA_TRUE;
13651      }
13652 
13653    /* IMPORTANT: Don't use cur1/cur2 past this point (because they probably
13654     * don't make sense anymore. That's why there are start and end),
13655     * unless you know what you are doing */
13656 
13657    /* Special case when they share the same item and it's a text item */
13658    if ((it1 == it2) && (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT))
13659      {
13660         Evas_Coord x1, w1, x2, w2;
13661         Evas_Coord x, w, y, h;
13662         Evas_Object_Textblock_Text_Item *ti;
13663         int ret = 0;
13664 
13665         ti = _ITEM_TEXT(it1);
13666         if (ti->parent.format->font.font)
13667           {
13668              ret = ENFN->font_pen_coords_get(ENC,
13669                    ti->parent.format->font.font,
13670                    &ti->text_props,
13671                    start,
13672                    &x1, &y, &w1, &h);
13673           }
13674         if (!ret)
13675           {
13676              return NULL;
13677           }
13678         ret = ENFN->font_pen_coords_get(ENC,
13679               ti->parent.format->font.font,
13680               &ti->text_props,
13681               end,
13682               &x2, &y, &w2, &h);
13683         if (!ret)
13684           {
13685              return NULL;
13686           }
13687 
13688 #ifdef BIDI_SUPPORT
13689         if (ti->text_props.bidi_dir == EVAS_BIDI_DIRECTION_RTL)
13690           {
13691              x1 = x1 + w1;
13692              x2 = x2 + w2;
13693           }
13694 #endif
13695 
13696         /* Make x2 the one on the right */
13697         if (x2 < x1)
13698           {
13699              Evas_Coord tmp;
13700              tmp = x1;
13701              x1 = x2;
13702              x2 = tmp;
13703           }
13704 
13705         x = x1;
13706         w = x2 - x1;
13707 
13708         if (w > 0)
13709           {
13710              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
13711              rects = eina_list_append(rects, tr);
13712              tr->x = ln->x + it1->x + x;
13713              tr->y = ln->par->y + ln->y;
13714              tr->h = ln->h;
13715              tr->w = w;
13716           }
13717      }
13718    else if ((it1 == it2) && (it1->type != EVAS_TEXTBLOCK_ITEM_TEXT))
13719      {
13720         Evas_Coord x, w;
13721         x = 0;
13722         w = it1->w;
13723         _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
13724                                        switch_items);
13725         if (w > 0)
13726           {
13727              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
13728              rects = eina_list_append(rects, tr);
13729              tr->x = ln->x + it1->x + x;
13730              tr->y = ln->par->y + ln->y;
13731              tr->h = ln->h;
13732              tr->w = w;
13733           }
13734      }
13735    else if (it1 != it2)
13736      {
13737         /* Get the middle items */
13738         Evas_Coord min_x, max_x;
13739         Evas_Coord x, w;
13740         it = _ITEM(EINA_INLIST_GET(it1)->next);
13741         min_x = max_x = it->x;
13742 
13743         if (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT)
13744           {
13745              Evas_Coord y, h;
13746              Evas_Object_Textblock_Text_Item *ti;
13747              int ret;
13748              ti = _ITEM_TEXT(it1);
13749 
13750              ret = ENFN->font_pen_coords_get(ENC,
13751                    ti->parent.format->font.font,
13752                    &ti->text_props,
13753                    start,
13754                    &x, &y, &w, &h);
13755              if (!ret)
13756                {
13757                   /* BUG! Skip the first item */
13758                   x = w = 0;
13759                }
13760              else
13761                {
13762                   _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
13763                         switch_items);
13764                }
13765           }
13766         else
13767           {
13768              x = 0;
13769              w = it1->w;
13770              _evas_textblock_range_calc_x_w(it1, &x, &w, EINA_TRUE,
13771                    switch_items);
13772           }
13773         if (w > 0)
13774           {
13775              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
13776              rects = eina_list_append(rects, tr);
13777              tr->x = ln->x + it1->x + x;
13778              tr->y = ln->par->y + ln->y;
13779              tr->h = ln->h;
13780              tr->w = w;
13781           }
13782 
13783         while (it && (it != it2))
13784           {
13785              if (((it1->text_pos <= it->text_pos) && (it->text_pos <= it2->text_pos)) ||
13786                    ((it2->text_pos <= it->text_pos) && (it->text_pos <= it1->text_pos)))
13787                {
13788                   max_x = it->x + it->adv;
13789                }
13790              it = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(it)->next;
13791           }
13792         if (min_x != max_x)
13793           {
13794              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
13795              rects = eina_list_append(rects, tr);
13796              tr->x = ln->x + min_x;
13797              tr->y = ln->par->y + ln->y;
13798              tr->h = ln->h;
13799              tr->w = max_x - min_x;
13800           }
13801         if (it2->type == EVAS_TEXTBLOCK_ITEM_TEXT)
13802           {
13803              Evas_Coord y, h;
13804              Evas_Object_Textblock_Text_Item *ti;
13805              int ret;
13806              ti = _ITEM_TEXT(it2);
13807 
13808              ret = ENFN->font_pen_coords_get(ENC,
13809                    ti->parent.format->font.font,
13810                    &ti->text_props,
13811                    end,
13812                    &x, &y, &w, &h);
13813              if (!ret)
13814                {
13815                   /* BUG! skip the last item */
13816                   x = w = 0;
13817                }
13818              else
13819                {
13820                   _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
13821                         switch_items);
13822                }
13823           }
13824         else
13825           {
13826              if (end > 0)
13827                {
13828                   x = it2->adv;
13829                   w = 0;
13830                }
13831              else
13832                {
13833                   x = 0;
13834                   w = it2->adv;
13835                }
13836              _evas_textblock_range_calc_x_w(it2, &x, &w, EINA_FALSE,
13837                         switch_items);
13838           }
13839         if (w > 0)
13840           {
13841              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
13842              rects = eina_list_append(rects, tr);
13843              tr->x = ln->x + it2->x + x;
13844              tr->y = ln->par->y + ln->y;
13845              tr->h = ln->h;
13846              tr->w = w;
13847           }
13848      }
13849    return rects;
13850 }
13851 
13852 /* Helper that creates a selection rectangle to a given line.
13853  * The given 'inv' indicates an inverse behavior. */
13854 static Evas_Textblock_Rectangle *
_line_fill_rect_get(const Evas_Object_Textblock_Line * ln,Evas_Coord w,Evas_Coord lm,Evas_Coord rm,Eina_Bool inv)13855 _line_fill_rect_get(const Evas_Object_Textblock_Line *ln,
13856                     Evas_Coord w, Evas_Coord lm, Evas_Coord rm, Eina_Bool inv)
13857 {
13858    Evas_Textblock_Rectangle *tr;
13859 
13860    tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
13861    tr->y = ln->par->y + ln->y;
13862    tr->h = ln->h;
13863 
13864    //Reminder: ln->x includes the left margin */
13865    if ((!inv && (ln->par->direction == EVAS_BIDI_DIRECTION_RTL)) ||
13866        (inv && (ln->par->direction != EVAS_BIDI_DIRECTION_RTL)))
13867      {
13868         tr->x = lm;
13869         tr->w = ln->x - lm;
13870      }
13871    else
13872      {
13873         tr->x = ln->x + ln->w;
13874         tr->w = w - rm - tr->x;
13875      }
13876 
13877    if (tr->w == 0)
13878      {
13879         free(tr);
13880         tr = NULL;
13881      }
13882    return tr;
13883 }
13884 
13885 EAPI Eina_Iterator *
evas_textblock_cursor_range_simple_geometry_get(const Efl_Text_Cursor_Handle * cur1,const Evas_Textblock_Cursor * cur2)13886 evas_textblock_cursor_range_simple_geometry_get(const Efl_Text_Cursor_Handle *cur1, const Evas_Textblock_Cursor *cur2)
13887 {
13888    if (!cur1) return NULL;
13889    Evas_Object_Textblock_Line *ln1, *ln2;
13890    Evas_Object_Textblock_Item *it1, *it2;
13891    Eina_List *rects = NULL;
13892    Eina_Iterator *itr = NULL;
13893 
13894    if (!cur1 || !cur1->node) return NULL;
13895    if (!cur2 || !cur2->node) return NULL;
13896    if (cur1->obj != cur2->obj) return NULL;
13897    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur1->obj, EFL_CANVAS_OBJECT_CLASS);
13898    evas_object_async_block(obj);
13899    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur1->obj, MY_CLASS);
13900    _relayout_if_needed(cur1->obj, o);
13901 
13902    if (evas_textblock_cursor_compare(cur1, cur2) > 0)
13903      {
13904         const Efl_Text_Cursor_Handle *tc;
13905 
13906         tc = cur1;
13907         cur1 = cur2;
13908         cur2 = tc;
13909      }
13910 
13911    ln1 = ln2 = NULL;
13912    it1 = it2 = NULL;
13913    _find_layout_item_match(cur1, &ln1, &it1);
13914    if (!ln1 || !it1) return NULL;
13915    _find_layout_item_match(cur2, &ln2, &it2);
13916    if (!ln2 || !it2) return NULL;
13917 
13918    if (ln1 == ln2)
13919      {
13920         rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1, cur1, cur2);
13921      }
13922    else
13923      {
13924         int lm = 0, rm = 0;
13925         Eina_List *rects2 = NULL;
13926         Evas_Coord w;
13927         Evas_Textblock_Rectangle *tr;
13928 
13929         evas_object_geometry_get(cur1->obj, NULL, NULL, &w, NULL);
13930 
13931         /* Use the minimum left margin and right margin for a uniform
13932          * line coverage of the rectangles */
13933         if (ln1->items)
13934           {
13935              Evas_Object_Textblock_Format *fm = ln1->items->format;
13936              if (fm)
13937                {
13938                   lm = fm->margin.l;
13939                   rm = fm->margin.r;
13940                }
13941           }
13942 
13943         if (ln2->items)
13944           {
13945              Evas_Object_Textblock_Format *fm = ln2->items->format;
13946              if (fm)
13947                {
13948                   if (fm->margin.l < lm) lm = fm->margin.l;
13949                   if (fm->margin.r < rm) rm = fm->margin.r;
13950                }
13951           }
13952 
13953         /* Append the rectangles by visual order (top, middle, bottom). Keep
13954          * it like that so it is also easier to test and debug. */
13955 
13956         /* Top line */
13957         rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1, cur1, NULL);
13958         /* Fill-in the top line */
13959         tr = _line_fill_rect_get(ln1, w, lm, rm, EINA_FALSE);
13960         if (tr)
13961           {
13962              rects = eina_list_append(rects, tr);
13963           }
13964 
13965         /* Middle rect (lines) */
13966         if ((ln1->par->y + ln1->y + ln1->h) != (ln2->par->y + ln2->y))
13967           {
13968              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
13969              tr->x = lm;
13970              tr->y = ln1->par->y + ln1->y + ln1->h;
13971              tr->w = w - tr->x - rm;
13972              tr->h = ln2->par->y + ln2->y - tr->y;
13973              rects = eina_list_append(rects, tr);
13974           }
13975 
13976         /* Bottom line */
13977         rects2 = _evas_textblock_cursor_range_in_line_geometry_get(ln2, NULL, cur2);
13978         /* Fill-in the bottom line */
13979         tr = _line_fill_rect_get(ln2, w, lm, rm, EINA_TRUE);
13980         if (tr)
13981           {
13982              rects2 = eina_list_append(rects2, tr);
13983           }
13984         rects = eina_list_merge(rects, rects2);
13985      }
13986    itr = _evas_textblock_selection_iterator_new(rects);
13987 
13988    return itr;
13989 }
13990 
13991 static Eina_List *
_efl_canvas_textblock_range_geometry_list_get(Eo * eo_obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o,const Efl_Text_Cursor_Handle * cur1,const Evas_Textblock_Cursor * cur2)13992 _efl_canvas_textblock_range_geometry_list_get(Eo *eo_obj EINA_UNUSED,
13993       Efl_Canvas_Textblock_Data *o, const Efl_Text_Cursor_Handle *cur1, const
13994       Evas_Textblock_Cursor *cur2)
13995 {
13996    Evas_Object_Textblock_Line *ln1, *ln2;
13997    Evas_Object_Textblock_Item *it1, *it2;
13998    Eina_List *rects = NULL;
13999    Evas_Textblock_Rectangle *tr;
14000 
14001    if (!cur1 || !cur1->node) return NULL;
14002    if (!cur2 || !cur2->node) return NULL;
14003    if (cur1->obj != cur2->obj) return NULL;
14004    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur1->obj, EFL_CANVAS_OBJECT_CLASS);
14005    evas_object_async_block(obj);
14006 
14007    _relayout_if_needed(cur1->obj, o);
14008 
14009    if (evas_textblock_cursor_compare(cur1, cur2) > 0)
14010      {
14011         const Efl_Text_Cursor_Handle *tc;
14012 
14013         tc = cur1;
14014         cur1 = cur2;
14015         cur2 = tc;
14016      }
14017 
14018    ln1 = ln2 = NULL;
14019    it1 = it2 = NULL;
14020    _find_layout_item_match(cur1, &ln1, &it1);
14021    if (!ln1 || !it1) return NULL;
14022    _find_layout_item_match(cur2, &ln2, &it2);
14023    if (!ln2 || !it2) return NULL;
14024 
14025    if (ln1 == ln2)
14026      {
14027         rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
14028               cur1, cur2);
14029      }
14030    else
14031      {
14032         Evas_Object_Textblock_Line *plni, *lni;
14033         Eina_List *rects2 = NULL;
14034         /* Handle the first line */
14035         rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
14036               cur1, NULL);
14037 
14038         /* Handle the lines between the first and the last line */
14039         lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(ln1)->next;
14040         if (!lni && (ln1->par != ln2->par))
14041           {
14042              lni = ((Evas_Object_Textblock_Paragraph *)
14043                     EINA_INLIST_GET(ln1->par)->next)->lines;
14044           }
14045         while (lni && (lni != ln2))
14046           {
14047              tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
14048              rects = eina_list_append(rects, tr);
14049              tr->x = lni->x;
14050              tr->y = lni->par->y + lni->y;
14051              tr->h = lni->h;
14052              tr->w = lni->w;
14053              plni = lni;
14054              lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(lni)->next;
14055              if (!lni && (plni->par != ln2->par))
14056                {
14057                   lni = ((Evas_Object_Textblock_Paragraph *)
14058                      EINA_INLIST_GET(plni->par)->next)->lines;
14059                }
14060           }
14061         rects2 = _evas_textblock_cursor_range_in_line_geometry_get(ln2,
14062               NULL, cur2);
14063         rects = eina_list_merge(rects, rects2);
14064      }
14065    return rects;
14066 }
14067 
14068 EAPI Eina_List *
evas_textblock_cursor_range_geometry_get(const Efl_Text_Cursor_Handle * cur1,const Evas_Textblock_Cursor * cur2_obj)14069 evas_textblock_cursor_range_geometry_get(const Efl_Text_Cursor_Handle *cur1, const Evas_Textblock_Cursor *cur2_obj)
14070 {
14071    Efl_Canvas_Textblock_Data *o;
14072 
14073    if (!cur1) return NULL;
14074 
14075    o = efl_data_scope_get(cur1->obj, MY_CLASS);
14076 
14077    return _efl_canvas_textblock_range_geometry_list_get(cur1->obj, o, cur1, cur2_obj);
14078 }
14079 
14080 static Eina_Bool
_evas_textblock_cursor_format_item_geometry_get(const Efl_Text_Cursor_Handle * cur,Evas_Coord * cx,Evas_Coord * cy,Evas_Coord * cw,Evas_Coord * ch)14081 _evas_textblock_cursor_format_item_geometry_get(const Efl_Text_Cursor_Handle *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
14082 {
14083    Evas_Object_Textblock_Line *ln = NULL;
14084    Evas_Object_Textblock_Format_Item *fi;
14085    Evas_Object_Textblock_Item *it = NULL;
14086    Evas_Coord x, y, w, h;
14087 
14088    if (!cur) return EINA_FALSE;
14089    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
14090    evas_object_async_block(obj);
14091    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
14092 
14093    _relayout_if_needed(cur->obj, o);
14094 
14095    if (!_evas_textblock_cursor_format_is_visible_get(cur)) return EINA_FALSE;
14096    _find_layout_item_line_match(cur->obj, cur->node, cur->pos, &ln, &it);
14097    if (it && (it->type != EVAS_TEXTBLOCK_ITEM_FORMAT)) return EINA_FALSE;
14098    fi = _ITEM_FORMAT(it);
14099    if ((!ln) || (!fi)) return EINA_FALSE;
14100    x = ln->x + fi->parent.x;
14101    y = ln->par->y + ln->y + ln->baseline + fi->y;
14102    w = fi->parent.w;
14103    h = fi->parent.h;
14104    if (cx) *cx = x;
14105    if (cy) *cy = y;
14106    if (cw) *cw = w;
14107    if (ch) *ch = h;
14108    return EINA_TRUE;
14109 }
14110 
14111 EAPI Eina_Bool
evas_textblock_cursor_format_item_geometry_get(const Evas_Textblock_Cursor * cur,Evas_Coord * cx,Evas_Coord * cy,Evas_Coord * cw,Evas_Coord * ch)14112 evas_textblock_cursor_format_item_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
14113 {
14114    return _evas_textblock_cursor_format_item_geometry_get(cur, cx, cy, cw, ch);
14115 }
14116 
14117 EAPI Eina_Bool
evas_textblock_cursor_eol_get(const Evas_Textblock_Cursor * cur)14118 evas_textblock_cursor_eol_get(const Evas_Textblock_Cursor *cur)
14119 {
14120    Eina_Bool ret = EINA_FALSE;
14121    Efl_Text_Cursor_Handle cur2;
14122    if (!cur) return EINA_FALSE;
14123    Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
14124    evas_object_async_block(obj);
14125 
14126    _evas_textblock_cursor_init(&cur2, cur->obj);
14127    _evas_textblock_cursor_copy(&cur2, cur);
14128    evas_textblock_cursor_line_char_last(&cur2);
14129    if (cur2.pos == cur->pos)
14130      {
14131         ret = EINA_TRUE;
14132      }
14133    return ret;
14134 }
14135 
14136 /* general controls */
14137 EAPI Eina_Bool
evas_object_textblock_line_number_geometry_get(const Eo * eo_obj,int line,Evas_Coord * cx,Evas_Coord * cy,Evas_Coord * cw,Evas_Coord * ch)14138 evas_object_textblock_line_number_geometry_get(const Eo *eo_obj, int line, Evas_Coord *cx, Evas_Coord *cy, Evas_Coord *cw, Evas_Coord *ch)
14139 {
14140    EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, EINA_FALSE);
14141    Evas_Object_Textblock_Line *ln;
14142    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
14143    evas_object_async_block(obj);
14144    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
14145 
14146    _relayout_if_needed((Evas_Object *)eo_obj, o);
14147 
14148    ln = _find_layout_line_num(eo_obj, line);
14149    if (!ln) return EINA_FALSE;
14150    if (cx) *cx = ln->x;
14151    if (cy) *cy = ln->par->y + ln->y;
14152    if (cw) *cw = ln->w;
14153    if (ch) *ch = ln->h;
14154    return EINA_TRUE;
14155 }
14156 
14157 static void
_evas_object_textblock_clear(Evas_Object * eo_obj)14158 _evas_object_textblock_clear(Evas_Object *eo_obj)
14159 {
14160    Eina_List *l;
14161    Efl_Text_Cursor_Handle *cur;
14162    Efl_Text_Cursor_Handle *co;
14163 
14164    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
14165    evas_object_async_block(obj);
14166    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
14167    if (o->paragraphs)
14168      {
14169         _paragraphs_free(o, obj, o->paragraphs);
14170         o->paragraphs = NULL;
14171      }
14172 
14173    _nodes_clear(eo_obj);
14174    co = o->cursor;
14175    co->node = NULL;
14176    co->pos = 0;
14177    co->changed = EINA_TRUE;
14178    EINA_LIST_FOREACH(o->cursors, l, cur)
14179      {
14180         cur->node = NULL;
14181         cur->pos = 0;
14182         cur->changed = EINA_TRUE;
14183      }
14184 
14185    _evas_textblock_changed(o, eo_obj);
14186 }
14187 
14188 EAPI void
evas_object_textblock_clear(Evas_Object * eo_obj)14189 evas_object_textblock_clear(Evas_Object *eo_obj)
14190 {
14191    TB_HEAD();
14192    _evas_object_textblock_clear(eo_obj);
14193 
14194    /* Force recreation of everything for textblock.
14195     * FIXME: We have the same thing in other places, merge it... */
14196    evas_textblock_cursor_paragraph_first(o->cursor);
14197    evas_textblock_cursor_text_append(o->cursor, "");
14198 }
14199 
14200 EOLIAN static Eina_Size2D
_efl_canvas_textblock_size_formatted_get(const Eo * eo_obj,Efl_Canvas_Textblock_Data * o)14201 _efl_canvas_textblock_size_formatted_get(const Eo *eo_obj, Efl_Canvas_Textblock_Data *o)
14202 {
14203    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
14204    evas_object_async_block(obj);
14205    _relayout_if_needed(eo_obj, o);
14206 
14207    Eina_Size2D ret = {
14208         .w = o->formatted.w,
14209         .h = o->formatted.h,
14210    };
14211    return ret;
14212 }
14213 
14214 #ifdef BIDI_SUPPORT
14215 /**
14216  * @internal
14217  * Returns last item in the visual order of a given native line.
14218  * Similar to the code of _layout_line_reorder, but does only the
14219  * required work to get the last item in the visual order. There is
14220  * no need to actually reorder the items in the native line.
14221  * @param line the native line to
14222  * @param bidi_props bidi-props used for visual reorder
14223  */
14224 static Evas_Object_Textblock_Item *
_line_native_last_visual_get(const Eina_List * line,Evas_BiDi_Paragraph_Props * bidi_props)14225 _line_native_last_visual_get(const Eina_List *line,
14226       Evas_BiDi_Paragraph_Props *bidi_props)
14227 {
14228    Evas_Object_Textblock_Item *it, *last_it = NULL;
14229    EvasBiDiStrIndex *v_to_l = NULL;
14230    size_t len;
14231    Evas_Object_Textblock_Item *items = NULL;
14232    const Eina_List *i;
14233    size_t max_vpos = 0;
14234 
14235    if (line)
14236       items = (Evas_Object_Textblock_Item *)eina_list_data_get(line);
14237 
14238    if (items && bidi_props)
14239      {
14240         size_t start, end;
14241         start = _ITEM(eina_list_data_get(line))->text_pos;
14242         it = _ITEM(eina_list_data_get(eina_list_last(line)));
14243         end = it->text_pos + GET_ITEM_LEN(it);
14244 
14245         len = end - start;
14246         evas_bidi_props_reorder_line(NULL, start, len, bidi_props, &v_to_l);
14247 
14248         /* Get the last visual item in this line */
14249         EINA_LIST_FOREACH(line, i, it)
14250           {
14251              size_t vpos = evas_bidi_position_logical_to_visual(
14252                    v_to_l, len, it->text_pos - start);
14253              if ((it->w > 0) && (!last_it || (vpos >= max_vpos)))
14254                {
14255                   last_it = it;
14256                   max_vpos = vpos;
14257                }
14258           }
14259         if (v_to_l) free(v_to_l);
14260      }
14261    return last_it;
14262 }
14263 #endif
14264 
14265 static inline void
_size_native_calc_line_finalize(const Evas_Object * eo_obj,const Evas_Object_Textblock_Paragraph * par,Eina_List * items,Evas_Coord * ascent,Evas_Coord * descent,Evas_Coord * w,Textblock_Position position)14266 _size_native_calc_line_finalize(const Evas_Object *eo_obj,
14267       const Evas_Object_Textblock_Paragraph *par, Eina_List *items,
14268       Evas_Coord *ascent, Evas_Coord *descent,
14269       Evas_Coord *w, Textblock_Position position)
14270 {
14271    Evas_Object_Textblock_Item *it, *last_it = NULL;
14272    Eina_List *i;
14273    Eina_Bool is_bidi = EINA_FALSE;
14274    Evas_Object_Protected_Data *obj =
14275       efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
14276 
14277    it = eina_list_data_get(items);
14278    *w = 0;
14279 
14280    if (it)
14281      {
14282         Evas_Coord asc = 0, desc = 0;
14283         /* If there are no text items yet, calc ascent/descent
14284          * according to the current format. */
14285         if (it->format)
14286           {
14287              _layout_item_ascent_descent_adjust(obj, &asc, &desc,
14288                                                 it, it->format);
14289           }
14290 
14291         if (asc > *ascent)
14292            *ascent = asc;
14293         if (desc > *descent)
14294            *descent = desc;
14295 
14296         /* Add margins. */
14297         if (it->format)
14298            *w = it->format->margin.l + it->format->margin.r;
14299       }
14300 
14301 #ifdef BIDI_SUPPORT
14302    /* Get last item by visual order, because this paragraph is bidi */
14303    if (par->is_bidi)
14304      {
14305         /* bidi_props has already been updated in calling function */
14306         last_it = _line_native_last_visual_get(items, par->bidi_props);
14307         is_bidi = EINA_TRUE;
14308      }
14309 #else
14310    (void) par;
14311 #endif
14312 
14313    /* Adjust all the item sizes according to the final line size,
14314     * and update the x positions of all the items of the line. */
14315    EINA_LIST_FOREACH(items, i, it)
14316      {
14317         if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
14318           {
14319              Evas_Coord fw, fh, fy;
14320 
14321              Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
14322              if (!fi->formatme) goto loop_advance;
14323              _layout_calculate_format_item_size(obj, fi, ascent,
14324                    descent, &fy, &fw, &fh);
14325           }
14326         else
14327           {
14328              Evas_Coord maxasc = 0, maxdesc = 0;
14329              _layout_item_ascent_descent_adjust(obj, ascent, descent,
14330                    it, it->format);
14331              _layout_item_max_ascent_descent_calc(obj, &maxasc, &maxdesc,
14332                    it, position);
14333 
14334              if (maxasc > *ascent)
14335                 *ascent = maxasc;
14336              if (maxdesc > *descent)
14337                 *descent = maxdesc;
14338           }
14339 
14340 loop_advance:
14341         *w += it->adv;
14342 
14343         /* Update visible last item in the logical order */
14344         if (!is_bidi && (it->w > 0))
14345            last_it = it;
14346      }
14347 
14348    /* rectify width of line using the last item */
14349    if (last_it)
14350       *w += last_it->w - last_it->adv;
14351 }
14352 
14353 /* FIXME: doc */
14354 static void
_size_native_calc_paragraph_size(const Evas_Object * eo_obj,const Efl_Canvas_Textblock_Data * o,Evas_Object_Textblock_Paragraph * par,Textblock_Position * position,Evas_Coord * _w,Evas_Coord * _h)14355 _size_native_calc_paragraph_size(const Evas_Object *eo_obj,
14356       const Efl_Canvas_Textblock_Data *o,
14357       Evas_Object_Textblock_Paragraph *par,
14358       Textblock_Position *position,
14359       Evas_Coord *_w, Evas_Coord *_h)
14360 {
14361    Eina_List *i;
14362    Evas_Object_Textblock_Item *it;
14363    Eina_List *line_items = NULL;
14364    Evas_Coord w = 0, y = 0, wmax = 0, h = 0, ascent = 0, descent = 0;
14365    Evas_Object_Protected_Data *obj =
14366       efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
14367 
14368 #ifdef BIDI_SUPPORT
14369    if (par->is_bidi)
14370       _layout_update_bidi_props(o, par);
14371 #endif
14372    EINA_LIST_FOREACH(par->logical_items, i, it)
14373      {
14374         line_items = eina_list_append(line_items, it);
14375         if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
14376           {
14377              Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it);
14378              if (fi->item && (_IS_LINE_SEPARATOR(fi->item) ||
14379                       _IS_PARAGRAPH_SEPARATOR(o, fi->item)))
14380                {
14381                   _size_native_calc_line_finalize(eo_obj, par, line_items, &ascent,
14382                         &descent, &w, *position);
14383 
14384                   if (ascent + descent > h)
14385                      h = ascent + descent;
14386 
14387                   y += h;
14388                   if (w > wmax)
14389                      wmax = w;
14390                   h = 0;
14391                   ascent = descent = 0;
14392                   *position = TEXTBLOCK_POSITION_ELSE;
14393                   line_items = eina_list_free(line_items);
14394                }
14395              else
14396                {
14397                   Evas_Coord fw, fh, fy;
14398                   /* If there are no text items yet, calc ascent/descent
14399                    * according to the current format. */
14400                   if ((ascent + descent) == 0)
14401                      _layout_item_ascent_descent_adjust(obj, &ascent,
14402                            &descent, it, it->format);
14403 
14404                   _layout_calculate_format_item_size(obj, fi, &ascent,
14405                         &descent, &fy, &fw, &fh);
14406                }
14407           }
14408         else
14409           {
14410              _layout_item_ascent_descent_adjust(obj, &ascent,
14411                    &descent, it, it->format);
14412           }
14413      }
14414 
14415    if (!EINA_INLIST_GET(par)->next)
14416      {
14417         *position = (*position == TEXTBLOCK_POSITION_START) ?
14418            TEXTBLOCK_POSITION_SINGLE : TEXTBLOCK_POSITION_END;
14419      }
14420    _size_native_calc_line_finalize(eo_obj, par, line_items, &ascent, &descent, &w, *position);
14421 #ifdef BIDI_SUPPORT
14422    /* Clear the bidi props because we don't need them anymore. */
14423    if (par->bidi_props)
14424      {
14425         evas_bidi_paragraph_props_unref(par->bidi_props);
14426         par->bidi_props = NULL;
14427      }
14428 #endif
14429 
14430    if (*position == TEXTBLOCK_POSITION_START)
14431       *position = TEXTBLOCK_POSITION_ELSE;
14432 
14433    eina_list_free(line_items);
14434 
14435    /* Do the last addition */
14436    if (ascent + descent > h)
14437       h = ascent + descent;
14438 
14439    if (w > wmax)
14440       wmax = w;
14441 
14442    *_h = y + h;
14443    *_w = wmax;
14444 }
14445 
14446 EOLIAN static Eina_Size2D
_efl_canvas_textblock_size_native_get(const Eo * eo_obj,Efl_Canvas_Textblock_Data * o)14447 _efl_canvas_textblock_size_native_get(const Eo *eo_obj, Efl_Canvas_Textblock_Data *o)
14448 {
14449    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
14450    evas_object_async_block(obj);
14451    if (!o->native.valid)
14452      {
14453         Evas_Coord wmax = 0, hmax = 0;
14454         Evas_Object_Textblock_Paragraph *par;
14455         Textblock_Position position = TEXTBLOCK_POSITION_START;
14456         /* We just want the layout objects to update, should probably
14457          * split that. */
14458         _relayout_if_needed(eo_obj, o);
14459 
14460         EINA_INLIST_FOREACH(o->paragraphs, par)
14461           {
14462              Evas_Coord tw, th;
14463              _size_native_calc_paragraph_size(eo_obj, o, par, &position, &tw, &th);
14464              if (tw > wmax)
14465                 wmax = tw;
14466              hmax += th;
14467           }
14468 
14469         o->native.w = wmax;
14470         o->native.h = hmax;
14471 
14472         o->native.valid = 1;
14473         o->content_changed = 0;
14474         o->format_changed = EINA_FALSE;
14475      }
14476 
14477    Eina_Size2D ret = {
14478         .w = o->native.w,
14479         .h = o->native.h,
14480    };
14481    return ret;
14482 }
14483 
14484 EOLIAN static void
_efl_canvas_textblock_style_insets_get(const Eo * eo_obj,Efl_Canvas_Textblock_Data * o,Evas_Coord * l,Evas_Coord * r,Evas_Coord * t,Evas_Coord * b)14485 _efl_canvas_textblock_style_insets_get(const Eo *eo_obj, Efl_Canvas_Textblock_Data *o, Evas_Coord *l, Evas_Coord *r, Evas_Coord *t, Evas_Coord *b)
14486 {
14487    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
14488    evas_object_async_block(obj);
14489    _relayout_if_needed(eo_obj, o);
14490 
14491    if (l) *l = o->style_pad.l;
14492    if (r) *r = o->style_pad.r;
14493    if (t) *t = o->style_pad.t;
14494    if (b) *b = o->style_pad.b;
14495 }
14496 
14497 EOLIAN static void
_efl_canvas_textblock_efl_object_dbg_info_get(Eo * eo_obj,Efl_Canvas_Textblock_Data * o EINA_UNUSED,Efl_Dbg_Info * root)14498 _efl_canvas_textblock_efl_object_dbg_info_get(Eo *eo_obj, Efl_Canvas_Textblock_Data *o EINA_UNUSED, Efl_Dbg_Info *root)
14499 {
14500    efl_dbg_info_get(efl_super(eo_obj, MY_CLASS), root);
14501    if (!root) return;
14502    Efl_Dbg_Info *group = EFL_DBG_INFO_LIST_APPEND(root, MY_CLASS_NAME);
14503    Efl_Dbg_Info *node;
14504 
14505    const char *style;
14506    const char *text = NULL;
14507    char shorttext[48];
14508    const Evas_Textblock_Style *ts = NULL;
14509 
14510    ts = evas_object_textblock_style_get(eo_obj);
14511    style = evas_textblock_style_get(ts);
14512    text = evas_object_textblock_text_markup_get(eo_obj);
14513    strncpy(shorttext, text, 38);
14514    if (shorttext[37])
14515      strcpy(shorttext + 37, "\xe2\x80\xa6"); /* HORIZONTAL ELLIPSIS */
14516 
14517    EFL_DBG_INFO_APPEND(group, "Style", EINA_VALUE_TYPE_STRING, style);
14518    EFL_DBG_INFO_APPEND(group, "Text", EINA_VALUE_TYPE_STRING, shorttext);
14519 
14520      {
14521         Eina_Size2D size;
14522         size = efl_canvas_textblock_size_formatted_get(eo_obj);
14523         node = EFL_DBG_INFO_LIST_APPEND(group, "Formatted size");
14524         EFL_DBG_INFO_APPEND(node, "w", EINA_VALUE_TYPE_INT, size.w);
14525         EFL_DBG_INFO_APPEND(node, "h", EINA_VALUE_TYPE_INT, size.h);
14526      }
14527 
14528      {
14529         Eina_Size2D size;
14530         size = efl_canvas_textblock_size_native_get(eo_obj);
14531         node = EFL_DBG_INFO_LIST_APPEND(group, "Native size");
14532         EFL_DBG_INFO_APPEND(node, "w", EINA_VALUE_TYPE_INT, size.w);
14533         EFL_DBG_INFO_APPEND(node, "h", EINA_VALUE_TYPE_INT, size.h);
14534      }
14535 }
14536 
14537 /* all nice and private */
14538 static void
evas_object_textblock_init(Evas_Object * eo_obj)14539 evas_object_textblock_init(Evas_Object *eo_obj)
14540 {
14541    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
14542    Efl_Canvas_Textblock_Data *o;
14543    static Eina_Bool linebreak_init = EINA_FALSE;
14544 
14545    if (!linebreak_init)
14546      {
14547         linebreak_init = EINA_TRUE;
14548         init_linebreak();
14549         init_wordbreak();
14550         init_graphemebreak();
14551      }
14552 
14553    o = obj->private_data;
14554    Efl_Text_Cursor_Handle *co = o->cursor;
14555    co->obj = eo_obj;
14556    evas_object_textblock_text_markup_set(eo_obj, "");
14557 
14558    o->multiline = EINA_TRUE;
14559 #ifdef BIDI_SUPPORT
14560    o->inherit_paragraph_direction = EINA_TRUE;
14561 #endif
14562 }
14563 
14564 EOLIAN static void
_efl_canvas_textblock_efl_object_destructor(Eo * eo_obj,Efl_Canvas_Textblock_Data * o EINA_UNUSED)14565 _efl_canvas_textblock_efl_object_destructor(Eo *eo_obj, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
14566 {
14567    evas_object_textblock_free(eo_obj);
14568    efl_destructor(efl_super(eo_obj, MY_CLASS));
14569 }
14570 
14571 static void
evas_object_textblock_free(Evas_Object * eo_obj)14572 evas_object_textblock_free(Evas_Object *eo_obj)
14573 {
14574    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
14575    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
14576    Efl_Canvas_Textblock_Filter_Program *prg;
14577    Evas_Filter_Data_Binding *db;
14578    User_Style_Entry *use;
14579 
14580    _evas_object_textblock_clear(eo_obj);
14581    evas_object_textblock_style_set(eo_obj, NULL);
14582 
14583    EINA_LIST_FREE(o->styles, use)
14584      {
14585         Evas_Textblock_Style *ts = use->st;
14586         ts->objects = eina_list_remove(ts->objects, eo_obj);
14587         if (!ts->objects && (ts->delete_me || o->auto_styles))
14588           {
14589              _style_cache = eina_list_remove(_style_cache, ts);
14590              evas_textblock_style_free(ts);
14591           }
14592         free(use);
14593      }
14594 
14595    EINA_INLIST_FREE(o->gfx_filter.programs, prg)
14596      {
14597         EINA_INLIST_REMOVE(o->gfx_filter.programs, prg);
14598         evas_filter_program_del(prg->pgm);
14599         eina_stringshare_del(prg->name);
14600         eina_stringshare_del(prg->code);
14601         free(prg);
14602      }
14603    EINA_INLIST_FREE(o->gfx_filter.data_bindings, db)
14604      {
14605         EINA_INLIST_REMOVE(o->gfx_filter.data_bindings, db);
14606         eina_stringshare_del(db->name);
14607         eina_stringshare_del(db->value);
14608         free(db);
14609      }
14610    eina_hash_free(o->gfx_filter.sources);
14611 
14612    while (evas_object_textblock_style_user_peek(eo_obj))
14613      {
14614         evas_object_textblock_style_user_pop(eo_obj);
14615      }
14616    free(o->cursor);
14617    while (o->cursors)
14618      {
14619         Efl_Text_Cursor_Handle *cur;
14620 
14621         cur = (Efl_Text_Cursor_Handle *)o->cursors->data;
14622         o->cursors = eina_list_remove_list(o->cursors, o->cursors);
14623         free(cur);
14624      }
14625    if (o->repch) eina_stringshare_del(o->repch);
14626    if (o->ellip_ti)
14627      {
14628         _item_free(o, obj, NULL, _ITEM(o->ellip_ti));
14629      }
14630    if (o->bidi_delimiters) eina_stringshare_del(o->bidi_delimiters);
14631    _format_command_shutdown();
14632 
14633    if (o->default_format.default_style_str)
14634      free(o->default_format.default_style_str);
14635 
14636    /* remove obstacles */
14637    _obstacles_free(eo_obj, o);
14638    if (o->fit_content_config.p_size_array)
14639      {
14640         free(o->fit_content_config.p_size_array);
14641         o->fit_content_config.p_size_array = NULL;
14642      }
14643 
14644    _evas_clear_main_format(eo_obj, o);
14645 
14646 #ifdef HAVE_HYPHEN
14647   /* Hyphenation */
14648   if (o->hyphenating)
14649     {
14650        _dicts_hyphen_detach(eo_obj);
14651     }
14652 #endif
14653   free(o->utf8);
14654 }
14655 
14656 static inline Evas_Filter_Context *
_filter_context_get(Evas_Object_Textblock_Text_Item * ti)14657 _filter_context_get(Evas_Object_Textblock_Text_Item *ti)
14658 {
14659    if (!ti || !ti->gfx_filter) return NULL;
14660    return ti->gfx_filter->ctx;
14661 }
14662 
14663 static void
_filter_sync_end(Evas_Filter_Context * ctx,Eina_Bool success)14664 _filter_sync_end(Evas_Filter_Context *ctx, Eina_Bool success)
14665 {
14666    Text_Item_Filter *filter;
14667    Eina_Bool async;
14668 
14669    filter = evas_filter_context_data_get(ctx);
14670    EINA_SAFETY_ON_NULL_RETURN(filter);
14671 
14672    async = evas_filter_context_async_get(ctx);
14673    _image_safe_unref(filter->evas, filter->output, async);
14674 
14675    if (filter->ti)
14676      {
14677         // FIXME: LEAK HERE!
14678         filter->output = evas_filter_buffer_backing_get(ctx, EVAS_FILTER_BUFFER_OUTPUT_ID, EINA_FALSE);
14679         if (filter->ti->parent.format->gfx_filter)
14680           filter->ti->parent.format->gfx_filter->invalid = !success;
14681         // else just avoid sigsegv
14682         if (filter->ctx == ctx)
14683           {
14684              // release local ref
14685              evas_filter_context_unref(ctx);
14686              filter->ctx = NULL;
14687           }
14688      }
14689    else
14690      {
14691         free(filter);
14692      }
14693 
14694    // release run ref
14695    evas_filter_context_unref(ctx);
14696 }
14697 
14698 static void
_filter_post_render_cb(void * data)14699 _filter_post_render_cb(void *data)
14700 {
14701    Efl_Canvas_Textblock_Filter_Post_Render *post_data = data;
14702 
14703    _filter_sync_end(post_data->ctx, post_data->success);
14704    free(post_data);
14705 }
14706 
14707 static void
_filter_cb(Evas_Filter_Context * ctx,void * data,Eina_Bool success)14708 _filter_cb(Evas_Filter_Context *ctx, void *data, Eina_Bool success)
14709 {
14710    Efl_Canvas_Textblock_Filter_Post_Render *post_data;
14711    Evas_Public_Data *evas = data;
14712 
14713    if (!evas_filter_context_async_get(ctx))
14714      {
14715         _filter_sync_end(ctx, success);
14716         return;
14717      }
14718 
14719    post_data = calloc(1, sizeof(*post_data));
14720    post_data->success = success;
14721    post_data->ctx = ctx;
14722    evas_post_render_job_add(evas, _filter_post_render_cb, post_data);
14723 }
14724 
14725 static inline Eina_Rectangle
_filter_relative_bounding_box_get(const Text_Item_Filter * tif)14726 _filter_relative_bounding_box_get(const Text_Item_Filter *tif)
14727 {
14728    int x_offset, y_offset, l, t, b;
14729    Eina_Rectangle rect;
14730 
14731    x_offset = tif->ti->parent.ln->x + tif->ti->parent.x;
14732    y_offset = tif->ti->parent.ln->par->y + tif->ti->parent.ln->y;
14733    l = tif->ti->parent.format->gfx_filter->pad.l;
14734    /* r = tif->ti->parent.format->gfx_filter->pad.r; */
14735    t = tif->ti->parent.format->gfx_filter->pad.t;
14736    b = tif->ti->parent.format->gfx_filter->pad.b;
14737 
14738    rect.x = x_offset - l;
14739    rect.y = y_offset - t;
14740    rect.w = tif->ti->parent.w;
14741    rect.h = tif->ti->parent.h + t + b;
14742    return rect;
14743 }
14744 
14745 static void
_filter_output_cache_prune(Evas_Object_Protected_Data * obj,Efl_Canvas_Textblock_Data * o)14746 _filter_output_cache_prune(Evas_Object_Protected_Data *obj, Efl_Canvas_Textblock_Data *o)
14747 {
14748    Text_Item_Filter *tif;
14749    Eina_Inlist *il;
14750    Eina_Rectangle obj_rect;
14751 
14752    // proxy surfaces contain the entire object, nothing to prune
14753    if (obj->proxy->proxies)
14754      return;
14755 
14756    obj_rect.x = obj->cur->cache.clip.x;
14757    obj_rect.y = obj->cur->cache.clip.y;
14758    obj_rect.w = obj->cur->cache.clip.w;
14759    obj_rect.h = obj->cur->cache.clip.h;
14760 
14761    EINA_INLIST_FOREACH_SAFE(o->gfx_filter.text_items, il, tif)
14762      {
14763         Eina_Rectangle it_rect;
14764 
14765         if (!tif->ti)
14766           {
14767              if (tif->ctx) continue;
14768           }
14769         else
14770           {
14771              if (!tif->ti->parent.ln || !tif->ti->parent.ln->par) continue;
14772 
14773              it_rect = _filter_relative_bounding_box_get(tif);
14774              it_rect.x += obj->cur->geometry.x;
14775              it_rect.y += obj->cur->geometry.y;
14776              if (eina_rectangles_intersect(&obj_rect, &it_rect)) continue;
14777           }
14778 
14779         _image_safe_unref(obj->layer->evas, tif->output, tif->do_async);
14780         tif->output = NULL;
14781 
14782      }
14783 }
14784 
14785 static inline Evas_Coord_Point
_filter_target_position_calc(Evas_Object_Protected_Data * obj,Evas_Object_Textblock_Text_Item * ti,int x,int y)14786 _filter_target_position_calc(Evas_Object_Protected_Data *obj,
14787                              Evas_Object_Textblock_Text_Item *ti, int x, int y)
14788 {
14789    Efl_Canvas_Textblock_Filter *filter = ti->parent.format->gfx_filter;
14790    Evas_Object_Textblock_Line *ln = ti->parent.ln;
14791    Evas_Coord_Point pt;
14792 
14793    pt.x = obj->cur->geometry.x + ln->x + ti->parent.x + x - filter->pad.l;
14794    pt.y = obj->cur->geometry.y + ln->par->y + ln->y + y - filter->pad.t - ti->parent.h + ln->h;
14795    return pt;
14796 }
14797 
14798 static void
evas_object_textblock_render(Evas_Object * eo_obj EINA_UNUSED,Evas_Object_Protected_Data * obj,void * type_private_data,void * engine,void * output,void * context,void * surface,int x,int y,Eina_Bool do_async)14799 evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED,
14800                              Evas_Object_Protected_Data *obj,
14801                              void *type_private_data,
14802                              void *engine, void *output, void *context, void *surface,
14803                              int x, int y, Eina_Bool do_async)
14804 {
14805    Evas_Object_Textblock_Paragraph *par, *start = NULL;
14806    Evas_Object_Textblock_Item *itr;
14807    Evas_Object_Textblock_Line *ln, *cur_ln = NULL;
14808    Efl_Canvas_Textblock_Data *o = type_private_data;
14809    ASYNC_BLOCK;
14810 
14811    Eina_List *shadows = NULL;
14812    Eina_List *glows = NULL;
14813    Eina_List *outlines = NULL;
14814    Eina_List *gfx_filters = NULL;
14815    void *context_save = context;
14816    int strikethrough_thickness, underline_thickness, underline_position;
14817    int i, j;
14818    int cx, cy, cw, ch, clip;
14819    int ca, cr, cg, cb;
14820    int na, nr, ng, nb;
14821    const char vals[5][5] =
14822      {
14823        {0, 1, 2, 1, 0},
14824        {1, 3, 4, 3, 1},
14825        {2, 4, 5, 4, 2},
14826        {1, 3, 4, 3, 1},
14827        {0, 1, 2, 1, 0}
14828      };
14829 
14830    /* FIXME: rare case when relayout was not called: cache.clip made
14831       the object not visible (eg. clipped out), but it is actually visible
14832       in this context (eg. inside a proxy).
14833       Plus, one more scenario is that the object isn't visible but actually is visible
14834       by evas_map. */
14835    if (o->changed || o->content_changed || o->format_changed || o->obstacle_changed)
14836      {
14837        _relayout_if_needed(eo_obj, o);
14838      }
14839 
14840    /* If there are no paragraphs and thus there are no lines,
14841     * there's nothing left to do. */
14842    if (!o->paragraphs)
14843      {
14844         return;
14845      }
14846 
14847    /* render object to surface with context, and offxet by x,y */
14848    ENFN->context_multiplier_unset(engine, context);
14849    ENFN->context_multiplier_set(engine, context, 0, 0, 0, 0);
14850    ENFN->context_render_op_set(engine, context, obj->cur->render_op);
14851    /* FIXME: This clipping is just until we fix inset handling correctly. */
14852    ENFN->context_clip_clip(engine, context,
14853                            obj->cur->geometry.x + x,
14854                            obj->cur->geometry.y + y,
14855                            obj->cur->geometry.w,
14856                            obj->cur->geometry.h);
14857    clip = ENFN->context_clip_get(engine, context, &cx, &cy, &cw, &ch);
14858 
14859    ENFN->context_color_set(engine, context, 0, 0, 0, 0);
14860    ca = cr = cg = cb = 0;
14861 
14862 #define ITEM_WALK() \
14863    EINA_INLIST_FOREACH(start, par) \
14864      { \
14865         if (!par->visible) continue; \
14866         if (clip) \
14867           { \
14868              if ((obj->cur->geometry.y + y + par->y + par->h) < (cy - 20)) \
14869              continue; \
14870              if ((obj->cur->geometry.y + y + par->y) > (cy + ch + 20)) \
14871              break; \
14872           } \
14873         _layout_paragraph_render(o, par); \
14874         EINA_INLIST_FOREACH(par->lines, ln) \
14875           { \
14876              if (clip) \
14877                { \
14878                   if ((obj->cur->geometry.y + y + par->y + ln->y + ln->h) < (cy - 20)) \
14879                   continue; \
14880                   if ((obj->cur->geometry.y + y + par->y + ln->y) > (cy + ch + 20)) \
14881                   break; \
14882                } \
14883              EINA_INLIST_FOREACH(ln->items, itr) \
14884                { \
14885                   Evas_Coord yoff; \
14886                   yoff = ln->baseline; \
14887                   if (!EINA_DBL_EQ(itr->format->valign, -1.0)) \
14888                     { \
14889                        if (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) \
14890                          { \
14891                             Evas_Object_Textblock_Text_Item *titr = \
14892                               (Evas_Object_Textblock_Text_Item *)itr; \
14893                             int ascent = 0; \
14894                             int descent = 0; \
14895                             if (titr->text_props.font_instance) \
14896                               { \
14897                                  ascent = evas_common_font_instance_ascent_get(titr->text_props.font_instance); \
14898                                  descent = evas_common_font_instance_descent_get(titr->text_props.font_instance); \
14899                               } \
14900                             yoff = ascent + \
14901                               (itr->format->valign * (ln->h - (ascent + descent))); \
14902                          } \
14903                        else yoff = itr->format->valign * (ln->h - itr->h); \
14904                     } \
14905                   itr->yoff = yoff;             \
14906                   if (clip) \
14907                     { \
14908                        if ((obj->cur->geometry.x + x + ln->x + itr->x + itr->w) < (cx - 20)) \
14909                        continue; \
14910                        if ((obj->cur->geometry.x + x + ln->x + itr->x) > (cx + cw + 20)) \
14911                        break; \
14912                     } \
14913                   if ((ln->x + itr->x + itr->w) <= 0) continue; \
14914                   if (ln->x + itr->x > obj->cur->geometry.w) break; \
14915                   do
14916 
14917 #define ITEM_WALK_END() \
14918                   while (0); \
14919                } \
14920           } \
14921      } \
14922    do {} while(0)
14923 
14924 #define COLOR_SET(col)                                                  \
14925    nr = obj->cur->cache.clip.r * ti->parent.format->color.col.r;        \
14926    ng = obj->cur->cache.clip.g * ti->parent.format->color.col.g;        \
14927    nb = obj->cur->cache.clip.b * ti->parent.format->color.col.b;        \
14928    na = obj->cur->cache.clip.a * ti->parent.format->color.col.a;        \
14929    if (na != ca || nb != cb || ng != cg || nr != cr)                    \
14930      {                                                                  \
14931         ENFN->context_color_set(engine, context,                        \
14932                                 nr / 255, ng / 255, nb / 255, na / 255); \
14933         cr = nr; cg = ng; cb = nb; ca = na;                             \
14934      }
14935 
14936 #define COLOR_SET_AMUL(col, amul)                                       \
14937    nr = obj->cur->cache.clip.r * ti->parent.format->color.col.r * (amul); \
14938    ng = obj->cur->cache.clip.g * ti->parent.format->color.col.g * (amul); \
14939    nb = obj->cur->cache.clip.b * ti->parent.format->color.col.b * (amul); \
14940    na = obj->cur->cache.clip.a * ti->parent.format->color.col.a * (amul); \
14941    if (na != ca || nb != cb || ng != cg || nr != cr)                    \
14942      {                                                                  \
14943         ENFN->context_color_set(engine, context,                        \
14944                                 nr / 65025, ng / 65025, nb / 65025, na / 65025); \
14945         cr = nr; cg = ng; cb = nb; ca = na;                             \
14946      }
14947 
14948 #define DRAW_TEXT_FILTER(gf, ox, oy) do {                               \
14949       evas_filter_input_render(eo_obj, ti->gfx_filter->ctx, engine, output, gf->dc, ti, \
14950                             gf->pad.l, gf->pad.r, gf->pad.t, gf->pad.b, \
14951                             (ox), (oy), do_async);                      \
14952    } while (0)
14953 
14954 #define DRAW_TEXT_NOFILTER(ox, oy) do {                                 \
14955    ENFN->context_cutout_target(engine, context,                         \
14956                                obj->cur->geometry.x + ln->x - (ln->h * 4) + ti->parent.x + x + (ox) - 100, \
14957                                obj->cur->geometry.y + ln->par->y + ln->y - ln->h + y + (oy), \
14958                                ti->parent.w + (ln->h * 8), ln->h * 3);  \
14959    evas_font_draw_async_check(obj, engine, output, context, surface,    \
14960      ti->parent.format->font.font,                                      \
14961      obj->cur->geometry.x + ln->x + ti->parent.x + x + (ox),            \
14962      obj->cur->geometry.y + ln->par->y + ln->y + yoff + y + (oy),       \
14963      ti->parent.w, ti->parent.h, ti->parent.w, ti->parent.h,            \
14964      &ti->text_props, do_async);                                        \
14965    } while (0)
14966 
14967 #define DRAW_TEXT(ox, oy) do {                                          \
14968    if (ti->parent.format->font.font)                                    \
14969      {                                                                  \
14970         if (EINA_LIKELY(!ti->gfx_filter || (!ti->gfx_filter->ctx && !ti->gfx_filter->output))) \
14971           DRAW_TEXT_NOFILTER(ox, oy);                                   \
14972         else if (ti->gfx_filter->ctx != NULL)                           \
14973           DRAW_TEXT_FILTER(ti->parent.format->gfx_filter, ox, oy);      \
14974      } } while(0)
14975 
14976    /* backing */
14977 #define DRAW_RECT(ox, oy, ow, oh, or, og, ob, oa)                       \
14978    do                                                                   \
14979      {                                                                  \
14980         nr = obj->cur->cache.clip.r * or;                               \
14981         ng = obj->cur->cache.clip.g * og;                               \
14982         nb = obj->cur->cache.clip.b * ob;                               \
14983         na = obj->cur->cache.clip.a * oa;                               \
14984         if (na != ca || nb != cb || ng != cg || nr != cr)               \
14985           {                                                             \
14986              ENFN->context_color_set(engine, context,                   \
14987                                      nr / 255, ng / 255, nb / 255, na / 255); \
14988              cr = nr; cg = ng; cb = nb; ca = na;                        \
14989           }                                                             \
14990         ENFN->rectangle_draw(engine,                                    \
14991                              output,                                    \
14992                              context,                                   \
14993                              surface,                                   \
14994                              obj->cur->geometry.x + ln->x + x + (ox),   \
14995                              obj->cur->geometry.y + ln->par->y + ln->y + y + (oy), \
14996                              (ow),                                      \
14997                              (oh),                                      \
14998                              do_async);                                 \
14999      }                                                                  \
15000    while (0)
15001 
15002 #define DRAW_FORMAT_DASHED(oname, oy, oh, dw, dp) \
15003    do \
15004      { \
15005         if (itr->format->oname) \
15006           { \
15007              unsigned char _or, _og, _ob, _oa; \
15008              int _ind, _dx = 0, _dn, _dr; \
15009              _or = itr->format->color.oname.r; \
15010              _og = itr->format->color.oname.g; \
15011              _ob = itr->format->color.oname.b; \
15012              _oa = itr->format->color.oname.a; \
15013              if (!EINA_INLIST_GET(itr)->next) \
15014                { \
15015                   _dn = itr->w / (dw + dp); \
15016                   _dr = itr->w % (dw + dp); \
15017                } \
15018              else \
15019                { \
15020                   _dn = itr->adv / (dw + dp); \
15021                   _dr = itr->adv % (dw + dp); \
15022                } \
15023              if (_dr > dw) _dr = dw; \
15024              for (_ind = 0 ; _ind < _dn ; _ind++) \
15025                { \
15026                   DRAW_RECT(itr->x + _dx, oy, dw, oh, _or, _og, _ob, _oa); \
15027                   _dx += dw + dp; \
15028                } \
15029              DRAW_RECT(itr->x + _dx, oy, _dr, oh, _or, _og, _ob, _oa); \
15030           } \
15031      } \
15032    while (0)
15033 
15034 #define DRAW_FORMAT(oname, oy, oh) \
15035    do \
15036      { \
15037         if (itr->format->oname) \
15038           { \
15039              unsigned char _or, _og, _ob, _oa; \
15040              _or = itr->format->color.oname.r; \
15041              _og = itr->format->color.oname.g; \
15042              _ob = itr->format->color.oname.b; \
15043              _oa = itr->format->color.oname.a; \
15044              DRAW_RECT(itr->x, oy, itr->adv, oh, _or, _og, _ob, _oa); \
15045           } \
15046      } \
15047    while (0)
15048 
15049      {
15050         Evas_Coord look_for_y = 0 - (obj->cur->geometry.y + y);
15051         if (clip)
15052           {
15053              Evas_Coord tmp_lfy = cy - (obj->cur->geometry.y + y);
15054              if (tmp_lfy > look_for_y)
15055                 look_for_y = tmp_lfy;
15056           }
15057 
15058         if (look_for_y >= 0)
15059            start = _layout_find_paragraph_by_y(o, look_for_y);
15060 
15061         if (!start)
15062            start = o->paragraphs;
15063      }
15064 
15065    ITEM_WALK()
15066      {
15067         /* Check which other pass are necessary to avoid useless WALK */
15068       Evas_Object_Textblock_Text_Item *ti;
15069 
15070       ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
15071       if (ti)
15072         {
15073            if (ti->parent.format->style & (EVAS_TEXT_STYLE_SHADOW |
15074                                            EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW |
15075                                            EVAS_TEXT_STYLE_OUTLINE_SHADOW |
15076                                            EVAS_TEXT_STYLE_FAR_SHADOW |
15077                                            EVAS_TEXT_STYLE_FAR_SOFT_SHADOW |
15078                                            EVAS_TEXT_STYLE_SOFT_SHADOW))
15079              {
15080                 shadows = eina_list_append(shadows, itr);
15081              }
15082            if ((ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC) ==
15083                EVAS_TEXT_STYLE_GLOW)
15084              {
15085                 glows = eina_list_append(glows, itr);
15086              }
15087            if (((ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC) == EVAS_TEXT_STYLE_OUTLINE) ||
15088                ((ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC) == EVAS_TEXT_STYLE_OUTLINE_SHADOW) ||
15089                ((ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC) == EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW) ||
15090                (ti->parent.format->style == EVAS_TEXT_STYLE_SOFT_OUTLINE))
15091              {
15092                 outlines = eina_list_append(outlines, itr);
15093              }
15094            if (ti->parent.format->gfx_filter)
15095              {
15096                 gfx_filters = eina_list_append(gfx_filters, itr);
15097              }
15098         }
15099 
15100         /* Draw background */
15101         DRAW_FORMAT(backing, 0, ln->h);
15102      }
15103    ITEM_WALK_END();
15104 
15105    /* There are size adjustments that depend on the styles drawn here back
15106     * in "_text_item_update_sizes" should not modify one without the other. */
15107 
15108    /* gfx filters preparation */
15109    EINA_LIST_FREE(gfx_filters, itr)
15110      {
15111         Efl_Canvas_Filter_State state = EFL_CANVAS_FILTER_STATE_DEFAULT;
15112         Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(itr);
15113         Efl_Canvas_Textblock_Filter *filter;
15114         Evas_Filter_Program *pgm;
15115         Evas_Filter_Context *ctx;
15116         Evas_Coord_Point target;
15117         Eina_Bool ok;
15118 
15119         ln = ti->parent.ln;
15120         filter = ti->parent.format->gfx_filter;
15121         pgm = _format_filter_program_get(o, ti->parent.format);
15122         if (!pgm)
15123           {
15124              WRN("Filter '%s' not found on this object", filter->name);
15125              continue;
15126           }
15127 
15128         if (!ti->gfx_filter)
15129           {
15130              ti->gfx_filter = calloc(1, sizeof(*ti->gfx_filter));
15131              ti->gfx_filter->evas = obj->layer->evas;
15132              ti->gfx_filter->ti = ti;
15133              EINA_INLIST_APPEND(o->gfx_filter.text_items, ti->gfx_filter);
15134           }
15135         else if (ti->gfx_filter->output)
15136           {
15137              if (!filter->redraw)
15138                {
15139                   if (obj->changed)
15140                     {
15141                        Evas_Filter_Proxy_Binding *pb;
15142                        Evas_Object_Protected_Data *source;
15143                        Eina_Iterator *iter;
15144 
15145                        iter = eina_hash_iterator_data_new(o->gfx_filter.sources);
15146                        EINA_ITERATOR_FOREACH(iter, pb)
15147                          {
15148                             source = efl_data_scope_get(pb->eo_source, EFL_CANVAS_OBJECT_CLASS);
15149                             if (source->changed)
15150                               {
15151                                  filter->redraw = EINA_TRUE;
15152                                  break;
15153                               }
15154                          }
15155                        eina_iterator_free(iter);
15156                     }
15157                   if (!filter->redraw) continue;
15158                }
15159 
15160              ENFN->image_free(engine, ti->gfx_filter->output);
15161              ti->gfx_filter->output = NULL;
15162           }
15163 
15164         ctx = evas_filter_context_new(obj->layer->evas, do_async, ti->gfx_filter);
15165         evas_filter_state_prepare(eo_obj, &state, ti);
15166         evas_filter_program_state_set(pgm, &state);
15167         ok = evas_filter_context_program_use(engine, output, ctx, pgm, EINA_FALSE, 0, 0);
15168         if (!ok)
15169           {
15170              evas_filter_context_unref(ctx);
15171              filter->invalid = EINA_TRUE;
15172              continue;
15173           }
15174 
15175         // target position
15176         evas_filter_program_padding_get(pgm, &filter->pad, NULL);
15177         target = _filter_target_position_calc(obj, ti, x, y);
15178         ENFN->context_color_set(engine, context, 255, 255, 255, 255);
15179         ENFN->context_multiplier_set(engine, context,
15180                                      obj->cur->cache.clip.r, obj->cur->cache.clip.g,
15181                                      obj->cur->cache.clip.b, obj->cur->cache.clip.a);
15182         evas_filter_context_proxy_render_all(ctx, eo_obj, output, EINA_FALSE);
15183         evas_filter_context_buffers_allocate_all(ctx);
15184         evas_filter_target_set(ctx, context, surface, target.x, target.y, NULL);
15185         if (ti->gfx_filter->ctx)
15186           evas_filter_context_unref(ti->gfx_filter->ctx);
15187         ti->gfx_filter->ctx = ctx;
15188         ti->gfx_filter->do_async = do_async;
15189 
15190         // common data for all items (FIXME: should be common to object)
15191         if (!filter->dc)
15192           {
15193              filter->dc = ENFN->context_new(engine);
15194              ENFN->context_color_set(engine, filter->dc, 255, 255, 255, 255);
15195           }
15196         filter->eo_obj = eo_obj;
15197         filter->evas = obj->layer->evas;
15198 
15199         ENFN->context_multiplier_unset(engine, context);
15200      }
15201 
15202    /* shadows */
15203    EINA_LIST_FREE(shadows, itr)
15204      {
15205         int shad_dst, shad_sz, dx, dy, haveshad;
15206         Evas_Object_Textblock_Text_Item *ti;
15207         Evas_Coord yoff;
15208 
15209         ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
15210         if (!ti) continue;
15211 
15212         yoff = itr->yoff;
15213         ln = itr->ln;
15214 
15215         if (EINA_UNLIKELY(_filter_context_get(ti) != NULL))
15216           context = ti->parent.format->gfx_filter->dc;
15217 
15218         shad_dst = shad_sz = dx = dy = haveshad = 0;
15219         switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC)
15220           {
15221            case EVAS_TEXT_STYLE_SHADOW:
15222               shad_dst = 1;
15223               haveshad = 1;
15224               break;
15225            case EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW:
15226               shad_dst = 1;
15227               shad_sz = 2;
15228               haveshad = 1;
15229               break;
15230            case EVAS_TEXT_STYLE_OUTLINE_SHADOW:
15231            case EVAS_TEXT_STYLE_FAR_SHADOW:
15232               shad_dst = 2;
15233               haveshad = 1;
15234               break;
15235            case EVAS_TEXT_STYLE_FAR_SOFT_SHADOW:
15236               shad_dst = 2;
15237               shad_sz = 2;
15238               haveshad = 1;
15239               break;
15240            case EVAS_TEXT_STYLE_SOFT_SHADOW:
15241               shad_dst = 1;
15242               shad_sz = 2;
15243               haveshad = 1;
15244               break;
15245            default:
15246               break;
15247           }
15248         if (haveshad)
15249           {
15250              if (shad_dst > 0)
15251                {
15252                   switch (ti->parent.format->style & EVAS_TEXT_STYLE_MASK_SHADOW_DIRECTION)
15253                     {
15254                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT:
15255                         dx = 1;
15256                         dy = 1;
15257                         break;
15258                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM:
15259                         dx = 0;
15260                         dy = 1;
15261                         break;
15262                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT:
15263                         dx = -1;
15264                         dy = 1;
15265                         break;
15266                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT:
15267                         dx = -1;
15268                         dy = 0;
15269                         break;
15270                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT:
15271                         dx = -1;
15272                         dy = -1;
15273                         break;
15274                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP:
15275                         dx = 0;
15276                         dy = -1;
15277                         break;
15278                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT:
15279                         dx = 1;
15280                         dy = -1;
15281                         break;
15282                      case EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT:
15283                         dx = 1;
15284                         dy = 0;
15285                      default:
15286                         break;
15287                     }
15288                   dx *= shad_dst;
15289                   dy *= shad_dst;
15290                }
15291              switch (shad_sz)
15292                {
15293                 case 0:
15294                    COLOR_SET(shadow);
15295                    DRAW_TEXT(dx, dy);
15296                    break;
15297                 case 2:
15298                    for (j = 0; j < 5; j++)
15299                      {
15300                         for (i = 0; i < 5; i++)
15301                           {
15302                              if (vals[i][j] != 0)
15303                                {
15304                                   COLOR_SET_AMUL(shadow, vals[i][j] * 50);
15305                                   DRAW_TEXT(i - 2 + dx, j - 2 + dy);
15306                                }
15307                           }
15308                      }
15309                    break;
15310                 default:
15311                    break;
15312                }
15313           }
15314 
15315         context = context_save;
15316      }
15317 
15318    /* glows */
15319    EINA_LIST_FREE(glows, itr)
15320      {
15321         Evas_Object_Textblock_Text_Item *ti;
15322         Evas_Coord yoff;
15323 
15324         ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
15325         if (!ti) continue;
15326 
15327         yoff = itr->yoff;
15328         ln = itr->ln;
15329 
15330         if (EINA_UNLIKELY(_filter_context_get(ti) != NULL))
15331           context = ti->parent.format->gfx_filter->dc;
15332 
15333         if ((ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC) == EVAS_TEXT_STYLE_GLOW)
15334           {
15335              for (j = 0; j < 5; j++)
15336                {
15337                   for (i = 0; i < 5; i++)
15338                     {
15339                        if (vals[i][j] != 0)
15340                          {
15341                             COLOR_SET_AMUL(glow, vals[i][j] * 50);
15342                             DRAW_TEXT(i - 2, j - 2);
15343                          }
15344                     }
15345                }
15346              COLOR_SET(glow2);
15347              DRAW_TEXT(-1, 0);
15348              DRAW_TEXT(1, 0);
15349              DRAW_TEXT(0, -1);
15350              DRAW_TEXT(0, 1);
15351           }
15352 
15353         context = context_save;
15354      }
15355 
15356    /* outlines */
15357    EINA_LIST_FREE(outlines, itr)
15358      {
15359         Evas_Object_Textblock_Text_Item *ti;
15360         Evas_Coord yoff;
15361 
15362         ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
15363         if (!ti) continue;
15364 
15365         yoff = itr->yoff;
15366         ln = itr->ln;
15367 
15368         if (EINA_UNLIKELY(_filter_context_get(ti) != NULL))
15369           context = ti->parent.format->gfx_filter->dc;
15370 
15371         if (((ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC) == EVAS_TEXT_STYLE_OUTLINE) ||
15372             ((ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC) == EVAS_TEXT_STYLE_OUTLINE_SHADOW) ||
15373             ((ti->parent.format->style & EVAS_TEXT_STYLE_MASK_BASIC) == EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW))
15374           {
15375              COLOR_SET(outline);
15376              DRAW_TEXT(-1, 0);
15377              DRAW_TEXT(1, 0);
15378              DRAW_TEXT(0, -1);
15379              DRAW_TEXT(0, 1);
15380           }
15381         else if (ti->parent.format->style == EVAS_TEXT_STYLE_SOFT_OUTLINE)
15382           {
15383              for (j = 0; j < 5; j++)
15384                {
15385                   for (i = 0; i < 5; i++)
15386                     {
15387                        if (((i != 2) || (j != 2)) && (vals[i][j] != 0))
15388                          {
15389                             COLOR_SET_AMUL(outline, vals[i][j] * 50);
15390                             DRAW_TEXT(i - 2, j - 2);
15391                          }
15392                     }
15393                }
15394           }
15395 
15396         context = context_save;
15397      }
15398 
15399    /* normal text and lines */
15400    /* Get the thickness, and save it for strikethrough of non-text items. */
15401    strikethrough_thickness = underline_thickness = evas_common_font_instance_underline_thickness_get(NULL);
15402    underline_position = evas_common_font_instance_underline_position_get(NULL);
15403    ENFN->context_multiplier_unset(engine, context);
15404 
15405    if (obj->cur->clipper)
15406      ENFN->context_multiplier_set(engine, context,
15407                                   obj->cur->clipper->cur->cache.clip.r,
15408                                   obj->cur->clipper->cur->cache.clip.g,
15409                                   obj->cur->clipper->cur->cache.clip.b,
15410                                   obj->cur->clipper->cur->cache.clip.a);
15411 
15412    ITEM_WALK()
15413      {
15414         Evas_Object_Textblock_Text_Item *ti;
15415 
15416         if (cur_ln != ln)
15417           {
15418              Evas_Object_Textblock_Item *itrr;
15419 
15420              cur_ln = ln;
15421              underline_thickness =
15422                 evas_common_font_instance_underline_thickness_get(NULL);
15423              underline_position =
15424                 evas_common_font_instance_underline_position_get(NULL);
15425 
15426              EINA_INLIST_FOREACH(ln->items, itrr)
15427                {
15428                   if (itrr->type == EVAS_TEXTBLOCK_ITEM_TEXT)
15429                     {
15430                        int fi_underline_thickness, fi_underline_position;
15431                        void *fi = _ITEM_TEXT(itrr)->text_props.font_instance;
15432 
15433                        fi_underline_thickness =
15434                           evas_common_font_instance_underline_thickness_get(fi);
15435                        fi_underline_position =
15436                           evas_common_font_instance_underline_position_get(fi);
15437 
15438                        if (fi_underline_thickness > underline_thickness)
15439                          underline_thickness = fi_underline_thickness;
15440                        if (fi_underline_position > underline_position)
15441                          underline_position = fi_underline_position;
15442                     }
15443                }
15444           }
15445 
15446         ti = (itr->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(itr) : NULL;
15447 
15448         /* NORMAL TEXT */
15449         if (ti)
15450           {
15451              void *fi = _ITEM_TEXT(itr)->text_props.font_instance;
15452 
15453              if (EINA_UNLIKELY(_filter_context_get(ti) != NULL))
15454                context = ti->parent.format->gfx_filter->dc;
15455 
15456              COLOR_SET(normal);
15457              DRAW_TEXT(0, 0);
15458              strikethrough_thickness =
15459                 evas_common_font_instance_underline_thickness_get(fi);
15460 
15461              context = context_save;
15462 
15463              if (EINA_UNLIKELY(ti->parent.format->gfx_filter != NULL))
15464                {
15465                   Evas_Filter_Context *ctx = _filter_context_get(ti);
15466                   void *buffer = ti->gfx_filter ? ti->gfx_filter->output : NULL;
15467 
15468                   if (buffer)
15469                     {
15470                        Evas_Coord_Point target;
15471                        int W = 0, H = 0;
15472 
15473                        target = _filter_target_position_calc(obj, ti, x, y);
15474                        ca = cr = cb = cg = 255;
15475                        ENFN->context_color_set(engine, context, 255, 255, 255, 255);
15476                        ENFN->image_size_get(engine, buffer, &W, &H);
15477                        ENFN->image_draw(engine, output, context, surface, buffer,
15478                                         0, 0, W, H, target.x, target.y, W, H, 0, do_async);
15479                     }
15480                   else if (ctx)
15481                     {
15482                        evas_filter_context_post_run_callback_set(ctx, _filter_cb, obj->layer->evas);
15483                        evas_filter_context_run(engine, output, ctx);
15484                     }
15485                }
15486           }
15487 
15488         /* STRIKETHROUGH */
15489         DRAW_FORMAT(strikethrough, (ln->h / 2), strikethrough_thickness);
15490 
15491         /* UNDERLINE */
15492         DRAW_FORMAT(underline, ln->baseline + underline_position,
15493                                underline_thickness * itr->format->underline_height);
15494 
15495         /* UNDERLINE DASHED */
15496         DRAW_FORMAT_DASHED(underline_dash, ln->baseline + underline_position,
15497                          underline_thickness,
15498                          itr->format->underline_dash_width,
15499                          itr->format->underline_dash_gap);
15500 
15501         /* UNDERLINE2 */
15502         DRAW_FORMAT(underline2, ln->baseline + underline_position + underline_thickness +
15503               underline_position, underline_thickness);
15504      }
15505    ITEM_WALK_END();
15506    ENFN->context_multiplier_unset(engine, context);
15507 }
15508 
15509 EOLIAN static void
_efl_canvas_textblock_efl_canvas_filter_internal_filter_state_prepare(Eo * eo_obj,Efl_Canvas_Textblock_Data * pd EINA_UNUSED,Efl_Canvas_Filter_State * state,void * data)15510 _efl_canvas_textblock_efl_canvas_filter_internal_filter_state_prepare(
15511       Eo *eo_obj, Efl_Canvas_Textblock_Data *pd EINA_UNUSED, Efl_Canvas_Filter_State *state, void *data)
15512 {
15513    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
15514    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, EFL_CANVAS_TEXTBLOCK_CLASS);
15515    Evas_Object_Textblock_Text_Item *ti = data;
15516    Efl_Canvas_Textblock_Filter_Program *program;
15517    Evas_Filter_Padding pad = {};
15518 
15519 #define STATE_COLOR(dst, src) dst.r = src.r; dst.g = src.g; dst.b = src.b; dst.a = src.a
15520    STATE_COLOR(state->color, ti->parent.format->color.normal);
15521    STATE_COLOR(state->text.glow, ti->parent.format->color.glow);
15522    STATE_COLOR(state->text.glow2, ti->parent.format->color.glow2);
15523    STATE_COLOR(state->text.shadow, ti->parent.format->color.shadow);
15524    STATE_COLOR(state->text.outline, ti->parent.format->color.outline);
15525 #undef STATE_COLOR
15526 
15527    program = _filter_program_find(o, ti->parent.format->gfx_filter->name);
15528    if (program) evas_filter_program_padding_get(program->pgm, &pad, NULL);
15529    state->w = ti->parent.w; // + l + r; (already included)
15530    state->h = ti->parent.h + pad.t + pad.b;
15531    state->scale = obj->cur->scale;
15532 }
15533 
15534 EOLIAN static Eina_Bool
_efl_canvas_textblock_efl_canvas_filter_internal_filter_input_render(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * pd EINA_UNUSED,void * filter,void * engine,void * output,void * drawctx,void * data,int l,int r EINA_UNUSED,int t,int b EINA_UNUSED,int x,int y,Eina_Bool do_async)15535 _efl_canvas_textblock_efl_canvas_filter_internal_filter_input_render(
15536       Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *pd EINA_UNUSED, void *filter,
15537       void *engine, void *output, void *drawctx, void *data,
15538       int l, int r EINA_UNUSED, int t, int b EINA_UNUSED,
15539       int x, int y, Eina_Bool do_async)
15540 {
15541    Evas_Object_Textblock_Text_Item *ti = data;
15542 
15543    return evas_filter_font_draw(filter, engine, output, drawctx,
15544                                 EVAS_FILTER_BUFFER_INPUT_ID,
15545                                 ti->parent.format->font.font,
15546                                 x + l,
15547                                 y + t + ti->parent.yoff + ti->parent.h - ti->parent.ln->h,
15548                                 &ti->text_props, do_async);
15549 }
15550 
15551 EOLIAN static void
_efl_canvas_textblock_efl_canvas_filter_internal_filter_dirty(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * pd EINA_UNUSED)15552 _efl_canvas_textblock_efl_canvas_filter_internal_filter_dirty(
15553       Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *pd EINA_UNUSED)
15554 {
15555    WRN("Filter marked as dirty. NOT IMPLEMENTED!");
15556 }
15557 
15558 EOLIAN static void
_efl_canvas_textblock_efl_gfx_filter_filter_program_set(Eo * eo_obj,Efl_Canvas_Textblock_Data * pd,const char * code,const char * name)15559 _efl_canvas_textblock_efl_gfx_filter_filter_program_set(Eo *eo_obj, Efl_Canvas_Textblock_Data *pd, const char *code, const char *name)
15560 {
15561    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
15562    Efl_Canvas_Textblock_Filter_Program *prg;
15563 
15564    prg = _filter_program_find(pd, name);
15565    if (prg)
15566      {
15567         if (eina_streq(prg->code, code)) return;
15568      }
15569    else
15570      {
15571         prg = calloc(1, sizeof(*prg));
15572         prg->name = eina_stringshare_add(name);
15573         pd->gfx_filter.programs = (Efl_Canvas_Textblock_Filter_Program *)
15574               eina_inlist_append(EINA_INLIST_GET(pd->gfx_filter.programs), EINA_INLIST_GET(prg));
15575      }
15576    eina_stringshare_replace(&prg->code, code);
15577    prg->changed = EINA_TRUE;
15578 
15579    pd->format_changed = EINA_TRUE;
15580    _evas_textblock_invalidate_all(pd);
15581    _evas_textblock_changed(pd, eo_obj);
15582    evas_object_change(eo_obj, obj);
15583 }
15584 
15585 EOLIAN static void
_efl_canvas_textblock_efl_gfx_filter_filter_program_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * pd EINA_UNUSED,const char ** code EINA_UNUSED,const char ** name EINA_UNUSED)15586 _efl_canvas_textblock_efl_gfx_filter_filter_program_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *pd EINA_UNUSED,
15587                                                    const char **code EINA_UNUSED, const char **name EINA_UNUSED)
15588 {
15589    // FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
15590    ERR("Invalid API definition for this object! 'name' needs to be an @in or @inout value!");
15591 }
15592 
15593 static Evas_Filter_Data_Binding *
_filter_data_binding_find(Efl_Canvas_Textblock_Data * pd,const char * name)15594 _filter_data_binding_find(Efl_Canvas_Textblock_Data *pd, const char *name)
15595 {
15596    Evas_Filter_Data_Binding *db;
15597 
15598    if (!name) return NULL;
15599    EINA_INLIST_FOREACH(pd->gfx_filter.data_bindings, db)
15600      if (!strcmp(db->name, name))
15601        return db;
15602 
15603    return NULL;
15604 }
15605 
15606 EOLIAN static void
_efl_canvas_textblock_efl_gfx_filter_filter_data_set(Eo * obj,Efl_Canvas_Textblock_Data * pd,const char * name,const char * value,Eina_Bool execute)15607 _efl_canvas_textblock_efl_gfx_filter_filter_data_set(Eo *obj, Efl_Canvas_Textblock_Data *pd, const char *name, const char *value, Eina_Bool execute)
15608 {
15609    Efl_Canvas_Textblock_Filter_Program *prg;
15610    Evas_Filter_Data_Binding *db;
15611 
15612    if (!name) return;
15613    db = _filter_data_binding_find(pd, name);
15614    if (db)
15615      {
15616         if (eina_streq(db->value, value) && (db->execute == execute))
15617           return;
15618         if (!value)
15619           {
15620              EINA_INLIST_REMOVE(pd->gfx_filter.data_bindings, db);
15621              eina_stringshare_del(db->name);
15622              eina_stringshare_del(db->value);
15623              free(db);
15624              return;
15625           }
15626      }
15627    else if (!value)
15628      {
15629         return;
15630      }
15631    else
15632      {
15633         db = calloc(1, sizeof(*db));
15634         if (!db) return;
15635         pd->gfx_filter.data_bindings = (Evas_Filter_Data_Binding *)
15636               eina_inlist_append(EINA_INLIST_GET(pd->gfx_filter.data_bindings), EINA_INLIST_GET(db));
15637         db->name = eina_stringshare_add(name);
15638      }
15639    eina_stringshare_replace(&db->value, value);
15640    db->execute = execute;
15641 
15642    EINA_INLIST_FOREACH(pd->gfx_filter.programs, prg)
15643      {
15644         if (!prg->code) continue;
15645         if (strstr(prg->code, name))
15646           prg->changed = EINA_TRUE;
15647      }
15648 
15649    pd->format_changed = EINA_TRUE;
15650    _evas_textblock_invalidate_all(pd);
15651    _evas_textblock_changed(pd, obj);
15652    evas_object_change(obj, efl_data_scope_get(obj, EFL_CANVAS_OBJECT_CLASS));
15653 }
15654 
15655 EOLIAN static void
_efl_canvas_textblock_efl_gfx_filter_filter_data_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * pd,const char * name,const char ** value,Eina_Bool * execute)15656 _efl_canvas_textblock_efl_gfx_filter_filter_data_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *pd, const char *name, const char **value, Eina_Bool *execute)
15657 {
15658    Evas_Filter_Data_Binding *db;
15659 
15660    db = _filter_data_binding_find(pd, name);
15661    if (!db)
15662      {
15663         if (value) *value = NULL;
15664         if (execute) *execute = EINA_FALSE;
15665         return;
15666      }
15667 
15668    if (value) *value = db->value;
15669    if (execute) *execute = db->execute;
15670 }
15671 
15672 EOLIAN static void
_efl_canvas_textblock_efl_gfx_filter_filter_source_set(Eo * eo_obj,Efl_Canvas_Textblock_Data * pd,const char * name,Efl_Gfx_Entity * eo_source)15673 _efl_canvas_textblock_efl_gfx_filter_filter_source_set(Eo *eo_obj, Efl_Canvas_Textblock_Data *pd, const char *name, Efl_Gfx_Entity *eo_source)
15674 {
15675    Evas_Object_Protected_Data *obj, *source;
15676    Evas_Filter_Proxy_Binding *pb;
15677 
15678    if (!name) return;
15679 
15680    obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
15681    source = efl_data_scope_get(eo_source, EFL_CANVAS_OBJECT_CLASS);
15682    evas_object_async_block(obj);
15683 
15684    pb = eina_hash_find(pd->gfx_filter.sources, name);
15685    if (pb)
15686      {
15687         if (pb->eo_source == eo_source) return;
15688         eina_hash_del(pd->gfx_filter.sources, name, pb);
15689      }
15690    else if (!eo_source)
15691      {
15692         return;
15693      }
15694    else
15695      {
15696         pb = calloc(1, sizeof(*pb));
15697         if (!pb) return;
15698         pb->eo_proxy = eo_obj;
15699         pb->eo_source = eo_source;
15700         pb->name = eina_stringshare_add(name);
15701      }
15702 
15703    if (!pd->gfx_filter.sources)
15704      pd->gfx_filter.sources = eina_hash_string_small_new(_evas_filter_source_hash_free_cb);
15705    eina_hash_set(pd->gfx_filter.sources, name, pb);
15706 
15707    if (!eina_list_data_find(source->proxy->proxies, eo_obj))
15708      {
15709         EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, source->proxy, Evas_Object_Proxy_Data, source_write)
15710           source_write->proxies = eina_list_append(source_write->proxies, eo_obj);
15711         EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, source_write)
15712      }
15713 
15714    if (!obj->proxy->is_proxy)
15715      {
15716         EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, obj->proxy, Evas_Object_Proxy_Data, proxy_write)
15717           proxy_write->is_proxy = EINA_TRUE;
15718         EINA_COW_WRITE_END(evas_object_proxy_cow, obj->proxy, proxy_write)
15719      }
15720 
15721    pd->format_changed = EINA_TRUE;
15722    _evas_textblock_invalidate_all(pd);
15723    _evas_textblock_changed(pd, eo_obj);
15724    evas_object_change(eo_obj, obj);
15725 }
15726 
15727 EOLIAN static Efl_Gfx_Entity *
_efl_canvas_textblock_efl_gfx_filter_filter_source_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * pd,const char * name)15728 _efl_canvas_textblock_efl_gfx_filter_filter_source_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *pd, const char *name)
15729 {
15730    return eina_hash_find(pd->gfx_filter.sources, name);
15731 }
15732 
15733 
15734 static void
evas_object_textblock_coords_recalc(Evas_Object * eo_obj,Evas_Object_Protected_Data * obj,void * type_private_data)15735 evas_object_textblock_coords_recalc(Evas_Object *eo_obj,
15736                                     Evas_Object_Protected_Data *obj,
15737                                     void *type_private_data)
15738 {
15739    Efl_Canvas_Textblock_Data *o = type_private_data;
15740 
15741 #ifdef BIDI_SUPPORT
15742    if (o->inherit_paragraph_direction)
15743      {
15744         Evas_BiDi_Direction parent_dir = EVAS_BIDI_DIRECTION_NEUTRAL;
15745 
15746         if (obj->smart.parent)
15747           {
15748              parent_dir = evas_object_paragraph_direction_get(obj->smart.parent);
15749           }
15750 
15751         if (parent_dir != o->paragraph_direction)
15752           {
15753              o->paragraph_direction = parent_dir;
15754              o->changed_paragraph_direction = EINA_TRUE;
15755           }
15756      }
15757 #endif
15758 
15759    if (
15760        // width changed thus we may have to re-wrap or change centering etc.
15761        (obj->cur->geometry.w != o->last_w) ||
15762        // if valign not top OR we have ellipsis, then if height changed we need to re-eval valign or ... spot
15763        (((!EINA_DBL_EQ(o->valign, 0.0)) || (o->have_ellipsis)) &&
15764            (
15765                ((o->formatted.oneline_h == 0) &&
15766                    (obj->cur->geometry.h != o->last_h)) ||
15767                ((o->formatted.oneline_h != 0) &&
15768                    (((obj->cur->geometry.h != o->last_h) &&
15769                      (o->formatted.oneline_h < obj->cur->geometry.h))))
15770            )
15771        ) ||
15772        // obviously if content text changed we need to reformat it
15773        (o->content_changed) ||
15774        // if format changed (eg styles) we need to re-format/match tags etc.
15775        (o->format_changed) ||
15776        (o->obstacle_changed) ||
15777        (o->changed_paragraph_direction)
15778       )
15779      {
15780         LYDBG("ZZ: invalidate 2 %p ## %i != %i || %3.3f || %i && %i != %i | %i %i || %d\n", eo_obj, obj->cur->geometry.w, o->last_w, o->valign, o->have_ellipsis, obj->cur->geometry.h, o->last_h, o->content_changed, o->format_changed, o->changed_paragraph_direction);
15781 
15782         if (o->changed_paragraph_direction)
15783           {
15784              _evas_textblock_invalidate_all(o);
15785              _evas_textblock_changed(o, eo_obj);
15786           }
15787 
15788         o->formatted.valid = 0;
15789         o->changed = 1;
15790      }
15791 
15792    Evas_Coord x,y,w,h;
15793    evas_object_geometry_get(eo_obj, &x, &y, &w, &h);
15794    if (
15795        (w!=o->fit_content_config.last_size.w || h!=o->fit_content_config.last_size.h) &&
15796        (o->fit_content_config.options & TEXTBLOCK_FIT_MODE_ALL) != TEXTBLOCK_FIT_MODE_NONE
15797       )
15798      {
15799         fit_cache_clear(&o->fit_content_config, FIT_CACHE_INTERNAL_SIZE_ARRAY);
15800         fit_text_block(eo_obj);
15801      }
15802 }
15803 
15804 static void
evas_object_textblock_render_pre(Evas_Object * eo_obj,Evas_Object_Protected_Data * obj,void * type_private_data)15805 evas_object_textblock_render_pre(Evas_Object *eo_obj,
15806                                  Evas_Object_Protected_Data *obj,
15807                                  void *type_private_data)
15808 {
15809    Efl_Canvas_Textblock_Data *o = type_private_data;
15810    ASYNC_BLOCK;
15811 
15812    int is_v, was_v;
15813 
15814    /* dont pre-render the obj twice! */
15815    if (obj->pre_render_done) return;
15816    obj->pre_render_done = EINA_TRUE;
15817 
15818    /* pre-render phase. this does anything an object needs to do just before */
15819    /* rendering. this could mean loading the image data, retrieving it from */
15820    /* elsewhere, decoding video etc. */
15821    /* then when this is done the object needs to figure if it changed and */
15822    /* if so what and where and add the appropriate redraw textblocks */
15823 
15824    /* if someone is clipping this obj - go calculate the clipper */
15825    if (obj->cur->clipper)
15826      {
15827         if (obj->cur->cache.clip.dirty)
15828           evas_object_clip_recalc(obj->cur->clipper);
15829         obj->cur->clipper->func->render_pre(obj->cur->clipper->object,
15830                                             obj->cur->clipper,
15831                                             obj->cur->clipper->private_data);
15832      }
15833 
15834    //evas_object_textblock_coords_recalc(eo_obj, obj, obj->private_data);
15835    is_v = evas_object_is_visible(obj);
15836    was_v = evas_object_was_visible(obj);
15837    if (is_v && !_relayout_if_needed(eo_obj, o))
15838      {
15839         o->redraw = 0;
15840         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes,
15841                                             eo_obj, obj);
15842         goto done;
15843      }
15844    if (o->changed)
15845      {
15846         LYDBG("ZZ: relayout 16\n");
15847         o->redraw = 0;
15848         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes,
15849                                             eo_obj, obj);
15850         goto done;
15851      }
15852 
15853    if (o->redraw)
15854      {
15855         o->redraw = 0;
15856         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes,
15857                                             eo_obj, obj);
15858         goto done;
15859      }
15860    /* now figure what changed and add draw rects */
15861    /* if it just became visible or invisible */
15862    if (is_v != was_v)
15863      {
15864         evas_object_render_pre_visible_change(&obj->layer->evas->clip_changes,
15865                                               eo_obj, is_v, was_v);
15866         goto done;
15867      }
15868    if (obj->changed_map || obj->changed_src_visible)
15869      {
15870         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes,
15871                                             eo_obj, obj);
15872         goto done;
15873      }
15874    /* it's not visible - we accounted for it appearing or not so just abort */
15875    if (!is_v) goto done;
15876    /* clipper changed this is in addition to anything else for obj */
15877    evas_object_render_pre_clipper_change(&obj->layer->evas->clip_changes,
15878                                          eo_obj);
15879    /* if we restacked (layer or just within a layer) and don't clip anyone */
15880    if (obj->restack)
15881      {
15882         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes,
15883                                             eo_obj, obj);
15884         goto done;
15885      }
15886    /* if it changed color */
15887    if ((obj->cur->color.r != obj->prev->color.r) ||
15888        (obj->cur->color.g != obj->prev->color.g) ||
15889        (obj->cur->color.b != obj->prev->color.b) ||
15890        (obj->cur->color.a != obj->prev->color.a) ||
15891        (obj->cur->cache.clip.r != obj->prev->cache.clip.r) ||
15892        (obj->cur->cache.clip.g != obj->prev->cache.clip.g) ||
15893        (obj->cur->cache.clip.b != obj->prev->cache.clip.b) ||
15894        (obj->cur->cache.clip.a != obj->prev->cache.clip.a))
15895      {
15896         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes,
15897                                             eo_obj, obj);
15898         goto done;
15899      }
15900    /* if it changed geometry - and obviously not visibility or color */
15901    /* calculate differences since we have a constant color fill */
15902    /* we really only need to update the differences */
15903    if ((obj->cur->geometry.x != obj->prev->geometry.x) ||
15904        (obj->cur->geometry.y != obj->prev->geometry.y) ||
15905        (obj->cur->geometry.w != obj->prev->geometry.w) ||
15906        (obj->cur->geometry.h != obj->prev->geometry.h))
15907      {
15908         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes,
15909                                             eo_obj, obj);
15910         goto done;
15911      }
15912    if (obj->cur->render_op != obj->prev->render_op)
15913      {
15914         evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes,
15915                                             eo_obj, obj);
15916         goto done;
15917      }
15918 done:
15919    evas_object_render_pre_effect_updates(&obj->layer->evas->clip_changes,
15920                                          eo_obj, is_v, was_v);
15921 }
15922 
fit_style_update(Evas_Object * object,int i_font_size,Eina_Bool disable_ellipsis,Eina_Bool disable_wrap)15923 void fit_style_update(Evas_Object *object, int i_font_size, Eina_Bool disable_ellipsis, Eina_Bool disable_wrap)
15924 {
15925    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(object, MY_CLASS);
15926    TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config;
15927    memset(fc->fit_style,0,sizeof(fc->fit_style));
15928    char * fit_style = fc->fit_style;
15929    if (i_font_size >= 0)
15930      {
15931         char font_size[24];
15932         char *pfont = font_size;
15933         snprintf(font_size, sizeof(font_size), "font_size=%i ", i_font_size);
15934         while (*pfont)
15935           {
15936              *fit_style = *pfont;
15937              pfont++;
15938              fit_style++;
15939           }
15940      }
15941 
15942    if (disable_ellipsis == EINA_TRUE)
15943      {
15944         *fit_style = ' ';
15945         fit_style++;
15946         char *p = "ellipsis=2.0";
15947         while (*p)
15948           {
15949              *fit_style = *p;
15950              p++;
15951              fit_style++;
15952           }
15953      }
15954 
15955    if (disable_wrap == EINA_TRUE)
15956      {
15957         *fit_style = ' ';
15958         fit_style++;
15959         char *p = "wrap=none";
15960         while (*p)
15961           {
15962              *fit_style = *p;
15963              p++;
15964              fit_style++;
15965           }
15966      }
15967 
15968    _canvas_text_format_changed(object,o);
15969 }
15970 
15971 static void
evas_object_textblock_render_post(Evas_Object * eo_obj EINA_UNUSED,Evas_Object_Protected_Data * obj,void * type_private_data)15972 evas_object_textblock_render_post(Evas_Object *eo_obj EINA_UNUSED,
15973                                   Evas_Object_Protected_Data *obj,
15974                                   void *type_private_data)
15975 {
15976    Efl_Canvas_Textblock_Data *o = type_private_data;
15977    ASYNC_BLOCK;
15978 
15979    /* this moves the current data to the previous state parts of the object */
15980    /* in whatever way is safest for the object. also if we don't need object */
15981    /* data anymore we can free it if the object deems this is a good idea */
15982 /*   o = (Efl_Canvas_Textblock_Data *)(obj->object_data); */
15983    /* remove those pesky changes */
15984    evas_object_clip_changes_clean(obj);
15985    /* move cur to prev safely for object data */
15986    evas_object_cur_prev(obj);
15987 /*   o->prev = o->cur; */
15988    EINA_SAFETY_ON_NULL_RETURN(o);
15989    _filter_output_cache_prune(obj, o);
15990 }
15991 
evas_object_textblock_engine_data_get(Evas_Object * eo_obj)15992 static void *evas_object_textblock_engine_data_get(Evas_Object *eo_obj)
15993 {
15994    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
15995    if (!o) return NULL;
15996    return o->engine_data;
15997 }
15998 
15999 static int
evas_object_textblock_is_opaque(Evas_Object * eo_obj EINA_UNUSED,Evas_Object_Protected_Data * obj EINA_UNUSED,void * type_private_data EINA_UNUSED)16000 evas_object_textblock_is_opaque(Evas_Object *eo_obj EINA_UNUSED,
16001                                 Evas_Object_Protected_Data *obj EINA_UNUSED,
16002                                 void *type_private_data EINA_UNUSED)
16003 {
16004    /* this returns 1 if the internal object data implies that the object is */
16005    /* currently fully opaque over the entire gradient it occupies */
16006    return 0;
16007 }
16008 
16009 static int
evas_object_textblock_was_opaque(Evas_Object * eo_obj EINA_UNUSED,Evas_Object_Protected_Data * obj EINA_UNUSED,void * type_private_data EINA_UNUSED)16010 evas_object_textblock_was_opaque(Evas_Object *eo_obj EINA_UNUSED,
16011                                  Evas_Object_Protected_Data *obj EINA_UNUSED,
16012                                  void *type_private_data EINA_UNUSED)
16013 {
16014    /* this returns 1 if the internal object data implies that the object was */
16015    /* currently fully opaque over the entire gradient it occupies */
16016    return 0;
16017 }
16018 
16019 EOLIAN static void
_efl_canvas_textblock_efl_gfx_entity_scale_set(Evas_Object * eo_obj,Efl_Canvas_Textblock_Data * o,double scale)16020 _efl_canvas_textblock_efl_gfx_entity_scale_set(Evas_Object *eo_obj,
16021                                        Efl_Canvas_Textblock_Data *o,
16022                                        double scale)
16023 {
16024    if (EINA_DBL_EQ(efl_gfx_entity_scale_get(eo_obj), scale)) return;
16025    efl_gfx_entity_scale_set(efl_super(eo_obj, MY_CLASS), scale);
16026 
16027    _evas_textblock_invalidate_all(o);
16028    _evas_textblock_changed(o, eo_obj);
16029    o->last_w = -1;
16030    o->last_h = -1;
16031 }
16032 
16033 void
_evas_object_textblock_rehint(Evas_Object * eo_obj)16034 _evas_object_textblock_rehint(Evas_Object *eo_obj)
16035 {
16036    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
16037    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
16038    Evas_Object_Textblock_Paragraph *par;
16039    Evas_Object_Textblock_Line *ln;
16040 
16041    EINA_INLIST_FOREACH(o->paragraphs, par)
16042      {
16043         EINA_INLIST_FOREACH(par->lines, ln)
16044           {
16045              Evas_Object_Textblock_Item *it;
16046 
16047              EINA_INLIST_FOREACH(ln->items, it)
16048                {
16049                   if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
16050                     {
16051                        Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
16052                        if (ti->parent.format->font.font)
16053                          {
16054                             evas_font_load_hinting_set(ti->parent.format->font.font,
16055                                                        obj->layer->evas->hinting);
16056                          }
16057                     }
16058                }
16059           }
16060      }
16061    _evas_textblock_invalidate_all(o);
16062    _evas_textblock_changed(o, eo_obj);
16063 }
16064 
16065 EOLIAN static void
_efl_canvas_textblock_efl_canvas_object_paragraph_direction_set(Eo * eo_obj,Efl_Canvas_Textblock_Data * o,Efl_Text_Bidirectional_Type dir)16066 _efl_canvas_textblock_efl_canvas_object_paragraph_direction_set(Eo *eo_obj,
16067                                                            Efl_Canvas_Textblock_Data *o,
16068                                                            Efl_Text_Bidirectional_Type dir)
16069 {
16070 #ifdef BIDI_SUPPORT
16071    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
16072 
16073    if ((!(o->inherit_paragraph_direction) && (o->paragraph_direction == dir)) ||
16074        (o->inherit_paragraph_direction && (dir == EVAS_BIDI_DIRECTION_INHERIT)))
16075      return;
16076 
16077    if (dir == EVAS_BIDI_DIRECTION_INHERIT)
16078      {
16079         o->inherit_paragraph_direction = EINA_TRUE;
16080         Evas_BiDi_Direction parent_dir = EVAS_BIDI_DIRECTION_NEUTRAL;
16081 
16082         if (obj->smart.parent)
16083           parent_dir = evas_object_paragraph_direction_get(obj->smart.parent);
16084 
16085         if (parent_dir != o->paragraph_direction)
16086           {
16087              o->paragraph_direction = parent_dir;
16088              o->changed_paragraph_direction = EINA_TRUE;
16089              _evas_textblock_invalidate_all(o);
16090              _evas_textblock_changed(o, eo_obj);
16091           }
16092      }
16093    else
16094      {
16095         o->inherit_paragraph_direction = EINA_FALSE;
16096         o->paragraph_direction = dir;
16097         o->changed_paragraph_direction = EINA_TRUE;
16098         _evas_textblock_invalidate_all(o);
16099         _evas_textblock_changed(o, eo_obj);
16100      }
16101 #else
16102    (void) eo_obj;
16103    (void) o;
16104    (void) dir;
16105 #endif
16106 }
16107 
16108 EOLIAN static Efl_Text_Bidirectional_Type
_efl_canvas_textblock_efl_canvas_object_paragraph_direction_get(const Eo * eo_obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o)16109 _efl_canvas_textblock_efl_canvas_object_paragraph_direction_get(const Eo *eo_obj EINA_UNUSED,
16110                                                            Efl_Canvas_Textblock_Data *o)
16111 {
16112    return (Efl_Text_Bidirectional_Type)o->paragraph_direction;
16113 }
16114 
16115 EOLIAN static void
_efl_canvas_textblock_efl_text_text_set(Eo * eo_obj,Efl_Canvas_Textblock_Data * o,const char * text)16116 _efl_canvas_textblock_efl_text_text_set(Eo *eo_obj, Efl_Canvas_Textblock_Data *o,
16117       const char *text)
16118 {
16119    ASYNC_BLOCK;
16120    const char *old_txt = efl_text_get(eo_obj);
16121    Eina_Bool was_empty = (!old_txt || !(*old_txt));
16122 
16123    // Nothing to do
16124    if (was_empty && (!text || !(*text)))
16125      return;
16126 
16127    /*
16128     * Reduce changed events to one time emit only, in the appending phase
16129    */
16130    efl_event_freeze(eo_obj);
16131    evas_object_textblock_text_markup_set(eo_obj, "");
16132    efl_event_thaw(eo_obj);
16133 
16134    if (text && *text)
16135      _cursor_text_append(o->cursor, text);
16136    else if(!was_empty)
16137      efl_event_callback_call(eo_obj, EFL_CANVAS_TEXTBLOCK_EVENT_CHANGED, NULL);
16138 
16139    if (eo_obj)
16140      {
16141         _evas_textblock_changed(o, eo_obj);
16142      }
16143 }
16144 
16145 static char *
_canvas_text_get_all(const Eo * eo_obj,Efl_Canvas_Textblock_Data * o EINA_UNUSED)16146 _canvas_text_get_all(const Eo *eo_obj, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
16147 {
16148    Efl_Text_Cursor_Handle start, end;
16149 
16150    _evas_textblock_cursor_init(&start, eo_obj);
16151    _evas_textblock_cursor_init(&end, eo_obj);
16152 
16153    evas_textblock_cursor_paragraph_first(&start);
16154    evas_textblock_cursor_paragraph_last(&end);
16155 
16156    return _evas_textblock_cursor_range_text_get(&start, &end, EVAS_TEXTBLOCK_TEXT_PLAIN);
16157 }
16158 
16159 EOLIAN static const char *
_efl_canvas_textblock_efl_text_text_get(const Eo * eo_obj,Efl_Canvas_Textblock_Data * o)16160 _efl_canvas_textblock_efl_text_text_get(const Eo *eo_obj, Efl_Canvas_Textblock_Data *o)
16161 {
16162    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
16163    evas_object_async_block(obj);
16164 
16165    free(o->utf8);
16166    o->utf8 = _canvas_text_get_all(eo_obj, o);
16167    return o->utf8;
16168 }
16169 
evas_textblock_async_block(Evas_Object * eo_obj)16170 void evas_textblock_async_block(Evas_Object *eo_obj)
16171 {
16172    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
16173    ASYNC_BLOCK;
16174 }
16175 
16176 /**
16177   * @internal
16178   * Returns the value of the current data of list node,
16179   * and goes to the next list node.
16180   *
16181   * @param it the iterator.
16182   * @param data the data of the current list node.
16183   * @return EINA_FALSE if unsuccessful. Otherwise, returns EINA_TRUE.
16184   */
16185 Eina_Bool
_evas_textblock_annotation_iterator_next(Efl_Text_Attribute_Handle_Iterator * it,void ** data)16186 _evas_textblock_annotation_iterator_next(Efl_Text_Attribute_Handle_Iterator *it, void **data)
16187 {
16188    if (!it->current)
16189      return EINA_FALSE;
16190 
16191    *data = eina_list_data_get(it->current);
16192    it->current = eina_list_next(it->current);
16193 
16194    return EINA_TRUE;
16195 }
16196 
16197 /**
16198   * @internal
16199   * Frees the annotation iterator.
16200   * @param it the iterator to free
16201   * @return EINA_FALSE if unsuccessful. Otherwise, returns EINA_TRUE.
16202   */
16203 void
_evas_textblock_annotation_iterator_free(Efl_Text_Attribute_Handle_Iterator * it)16204 _evas_textblock_annotation_iterator_free(Efl_Text_Attribute_Handle_Iterator *it)
16205 {
16206    EINA_MAGIC_SET(&it->iterator, 0);
16207    it->current = NULL;
16208    eina_list_free(it->list);
16209    free(it);
16210 }
16211 
16212 /**
16213   * @internal
16214   * Creates newly allocated  iterator associated to a list.
16215   * @param list The list.
16216   * @return If the memory cannot be allocated, NULL is returned.
16217   * Otherwise, a valid iterator is returned.
16218   */
16219 Eina_Iterator *
_evas_textblock_annotation_iterator_new(Eina_List * list)16220 _evas_textblock_annotation_iterator_new(Eina_List *list)
16221 {
16222    Evas_Textblock_Selection_Iterator *it;
16223 
16224    it = calloc(1, sizeof(Efl_Text_Attribute_Handle_Iterator));
16225    if (!it) return NULL;
16226 
16227    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
16228    it->list = list;
16229    it->current = list;
16230 
16231    it->iterator.version = EINA_ITERATOR_VERSION;
16232    it->iterator.next = FUNC_ITERATOR_NEXT(
16233          _evas_textblock_annotation_iterator_next);
16234    it->iterator.free = FUNC_ITERATOR_FREE(
16235          _evas_textblock_annotation_iterator_free);
16236 
16237    return &it->iterator;
16238 }
16239 
16240 void
_textblock_cursor_pos_at_fnode_set(Efl_Text_Cursor_Handle * cur,Evas_Object_Textblock_Node_Format * fnode)16241 _textblock_cursor_pos_at_fnode_set(Efl_Text_Cursor_Handle *cur,
16242       Evas_Object_Textblock_Node_Format *fnode)
16243 {
16244    cur->node = fnode->text_node;
16245    cur->pos = _evas_textblock_node_format_pos_get(fnode);
16246 }
16247 
16248 Eina_Bool
_evas_textblock_annotations_set(Eo * eo_obj,Efl_Text_Attribute_Handle * an,Efl_Text_Cursor_Handle * start,Efl_Text_Cursor_Handle * end,const char * format,Eina_Bool is_item)16249 _evas_textblock_annotations_set(Eo *eo_obj,
16250       Efl_Text_Attribute_Handle *an,
16251       Efl_Text_Cursor_Handle *start, Efl_Text_Cursor_Handle *end,
16252       const char *format, Eina_Bool is_item)
16253 {
16254    int len;
16255    char *buf;
16256    Evas_Textblock_Node_Format *fnode;
16257    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
16258 
16259    if (an->is_item)
16260      {
16261         ERR("Cannot reset format of \"item\" annotations. This is a special"
16262              "annotation that should not be modified using this function");
16263         return EINA_FALSE;
16264      }
16265 
16266    /* Add opening format at 'start' */
16267    len = strlen(format);
16268    buf = malloc(len + 3);
16269    sprintf(buf, "<%s>", format);
16270    _evas_textblock_cursor_format_append(start, buf, &fnode, is_item);
16271    free(buf);
16272    an->start_node = fnode;
16273    fnode->annotation = an;
16274 
16275    /* Add a closing format at end (i.e. format does not apply at end) */
16276    len = strlen(format);
16277    buf = malloc(len + 4);
16278    sprintf(buf, "</%s>", format);
16279    if (is_item) evas_textblock_cursor_char_next(end);
16280    _evas_textblock_cursor_format_append(end, buf, &fnode, is_item);
16281    free(buf);
16282    an->end_node = fnode;
16283    fnode->annotation = an;
16284 
16285    o->format_changed = EINA_TRUE;
16286    return EINA_TRUE;
16287 }
16288 
16289 Eina_Inlist *
_evas_textblock_annotations_get(Eo * eo_obj)16290 _evas_textblock_annotations_get(Eo *eo_obj)
16291 {
16292    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
16293    return (Eina_Inlist*)o->annotations;
16294 }
16295 
16296 void
_evas_textblock_annotation_remove(Eo * eo_obj,Efl_Canvas_Textblock_Data * o,Efl_Text_Attribute_Handle * an,Eina_Bool remove_nodes,Eina_Bool invalidate)16297 _evas_textblock_annotation_remove(Eo *eo_obj, Efl_Canvas_Textblock_Data *o,
16298       Efl_Text_Attribute_Handle *an, Eina_Bool remove_nodes, Eina_Bool invalidate)
16299 {
16300    if (!o)
16301      o = efl_data_scope_get(eo_obj, MY_CLASS);
16302 
16303    if (remove_nodes)
16304      {
16305         if (an->is_item)
16306           {
16307              /* Remove the OBJ character along with the cursor. */
16308              Efl_Text_Cursor_Handle cur;
16309              _evas_textblock_cursor_init(&cur, an->obj);
16310              _textblock_cursor_pos_at_fnode_set(&cur, an->start_node);
16311              evas_textblock_cursor_char_delete(&cur);
16312              return; // 'an' should be deleted after char deletion.
16313           }
16314         _evas_textblock_node_format_remove(o, an->start_node, 0);
16315         _evas_textblock_node_format_remove(o, an->end_node, 0);
16316      }
16317    o->annotations = (Efl_Text_Attribute_Handle *)
16318       eina_inlist_remove(EINA_INLIST_GET(o->annotations),
16319             EINA_INLIST_GET(an));
16320    free(an);
16321    if (invalidate)
16322      {
16323         o->format_changed = EINA_TRUE;
16324 
16325         //XXX: It's a workaround. The underlying problem is that only new format
16326         // nodes are checks when their respective text nodes are invalidated (see
16327         // _format_changes_invalidate_text_nodes). Complete removal of the format
16328         // nodes was not handled properly (as formats could only be removed via
16329         // text changes e.g. deleting characters).
16330         _evas_textblock_invalidate_all(o);
16331 
16332         _evas_textblock_changed(o, eo_obj);
16333      }
16334 }
16335 
16336 void
_evas_textblock_annotations_clear(const Eo * eo_obj)16337 _evas_textblock_annotations_clear(const Eo *eo_obj)
16338 {
16339    Efl_Text_Attribute_Handle *an;
16340    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
16341 
16342    EINA_INLIST_FREE(o->annotations, an)
16343      {
16344         _evas_textblock_annotation_remove(NULL, o, an, EINA_TRUE, EINA_FALSE);
16345      }
16346 }
16347 
16348 Efl_Text_Attribute_Handle *
_evas_textblock_annotations_insert(Eo * eo_obj,Efl_Text_Cursor_Handle * start,Efl_Text_Cursor_Handle * end,const char * format,Eina_Bool is_item)16349 _evas_textblock_annotations_insert(Eo *eo_obj, Efl_Text_Cursor_Handle *start, Efl_Text_Cursor_Handle *end,
16350       const char *format, Eina_Bool is_item)
16351 {
16352    Efl_Text_Attribute_Handle *ret = NULL;
16353    Eina_Strbuf *buf;
16354    Eina_Bool first = EINA_TRUE;
16355    const char *item;
16356    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
16357 
16358    if (!format || (format[0] == '\0') ||
16359          evas_textblock_cursor_compare(start, end) > 0)
16360      {
16361         return NULL;
16362      }
16363 
16364    /* Sanitize the string and reject format items, closing '/' marks. */
16365    buf = eina_strbuf_new();
16366    while ((item = _format_parse(&format, EINA_FALSE)))
16367      {
16368         int itlen = format - item;
16369         /* We care about all of the formats even after a - except for
16370          * item which we don't care after a - because it's just a standard
16371          * closing */
16372         if ((!strncmp(item, "\n", itlen) || !strncmp(item, "\\n", itlen)) ||
16373               (!strncmp(item, "\t", itlen) || !strncmp(item, "\\t", itlen)) ||
16374               (!strncmp(item, "br", itlen) && (itlen >= 2)) ||
16375               (!strncmp(item, "tab", itlen) && (itlen >= 3)) ||
16376               (!strncmp(item, "ps", itlen) && (itlen >= 2)) ||
16377               (!strncmp(item, "item", itlen) && (itlen >= 4)))
16378           {
16379              continue;
16380           }
16381         if (first)
16382           {
16383              first = EINA_FALSE;
16384           }
16385         else
16386           {
16387              eina_strbuf_append_length(buf, " ", 1);
16388           }
16389         eina_strbuf_append_length(buf, item, itlen);
16390      }
16391 
16392    format = eina_strbuf_string_get(buf);
16393    if (format && (format[0] != '\0'))
16394      {
16395         ret = calloc(1, sizeof(Efl_Text_Attribute_Handle));
16396         ret->obj = eo_obj;
16397 
16398         o->annotations = (Efl_Text_Attribute_Handle *)
16399            eina_inlist_append(EINA_INLIST_GET(o->annotations),
16400                  EINA_INLIST_GET(ret));
16401 
16402 
16403         _evas_textblock_annotations_set(eo_obj, ret, start, end, format, is_item);
16404         ret->is_item = is_item;
16405         _evas_textblock_changed(o, eo_obj);
16406      }
16407 
16408    eina_strbuf_free(buf);
16409 
16410    return ret;
16411 }
16412 
16413 static void
_canvas_text_format_changed(Eo * eo_obj,Efl_Canvas_Textblock_Data * o)16414 _canvas_text_format_changed(Eo *eo_obj, Efl_Canvas_Textblock_Data *o)
16415 {
16416    o->format_changed = EINA_TRUE;
16417    _evas_clear_main_format(eo_obj, o);
16418    _evas_textblock_invalidate_all(o);
16419    _evas_textblock_changed(o, eo_obj);
16420    efl_event_callback_call(eo_obj, EFL_CANVAS_TEXTBLOCK_EVENT_CHANGED, NULL);
16421 }
16422 
16423 /* Efl.Text.Font interface implementation */
16424 
16425 static void
_efl_canvas_textblock_efl_text_font_properties_font_family_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,const char * font)16426 _efl_canvas_textblock_efl_text_font_properties_font_family_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, const char *font)
16427 {
16428    ASYNC_BLOCK;
16429    Eina_Bool changed = EINA_FALSE;
16430 
16431    Eina_Stringshare *nfont;
16432 
16433    EINA_SAFETY_ON_NULL_RETURN(font);
16434 
16435    if (_FMT_INFO(font) != font && !eina_streq(_FMT_INFO(font), font))
16436      {
16437         nfont = eina_stringshare_add(font);
16438         if (nfont == _FMT_INFO(font))
16439           {
16440              /* Already stringshared here, unref */
16441              eina_stringshare_del(nfont);
16442           }
16443         else
16444           {
16445              // Set immediately, load font later
16446              _FMT_INFO(font) = nfont;
16447              changed = EINA_TRUE;
16448           }
16449      }
16450 
16451    if (changed)
16452      {
16453         _canvas_text_format_changed(obj, o);
16454      }
16455 }
16456 
16457 static const char *
_efl_canvas_textblock_efl_text_font_properties_font_family_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o)16458 _efl_canvas_textblock_efl_text_font_properties_font_family_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o)
16459 {
16460    return o->default_format.info.font;
16461 }
16462 
16463 static void
_efl_canvas_textblock_efl_text_font_properties_font_size_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o,int size)16464 _efl_canvas_textblock_efl_text_font_properties_font_size_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o, int size)
16465 {
16466    ASYNC_BLOCK;
16467    EINA_SAFETY_ON_FALSE_RETURN(size > 0);
16468    if (o->default_format.info.size != size)
16469      {
16470         o->default_format.info.size = size;
16471         _canvas_text_format_changed(obj, o);
16472      }
16473 }
16474 
16475 static Efl_Font_Size
_efl_canvas_textblock_efl_text_font_properties_font_size_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o)16476 _efl_canvas_textblock_efl_text_font_properties_font_size_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o)
16477 {
16478    return o->default_format.info.size;
16479 }
16480 
16481 static void
_efl_canvas_textblock_efl_text_font_properties_font_source_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,const char * font_source EINA_UNUSED)16482 _efl_canvas_textblock_efl_text_font_properties_font_source_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, const char *font_source EINA_UNUSED)
16483 {
16484    Eina_Stringshare *nfont_source;
16485    if (o->default_format.info.font_source != font_source)
16486      {
16487         nfont_source = eina_stringshare_add(font_source);
16488         if (nfont_source == _FMT_INFO(font_source))
16489           {
16490              /* Already stringshared here, unref */
16491              eina_stringshare_del(nfont_source);
16492           }
16493         else
16494           {
16495              // Set immediately, load font_source later
16496              _FMT_INFO(font_source) = nfont_source;
16497              _canvas_text_format_changed(obj, o);
16498           }
16499      }
16500 }
16501 
16502 static const char*
_efl_canvas_textblock_efl_text_font_properties_font_source_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)16503 _efl_canvas_textblock_efl_text_font_properties_font_source_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
16504 {
16505    return _FMT_INFO(font_source);
16506 }
16507 
16508 static void
_efl_canvas_textblock_efl_text_font_properties_font_fallbacks_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,const char * font_fallbacks EINA_UNUSED)16509 _efl_canvas_textblock_efl_text_font_properties_font_fallbacks_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, const char *font_fallbacks EINA_UNUSED)
16510 {
16511    Eina_Stringshare *nfont_fallbacks;
16512    if (o->default_format.info.font_fallbacks != font_fallbacks)
16513      {
16514         nfont_fallbacks = eina_stringshare_add(font_fallbacks);
16515         if (nfont_fallbacks == _FMT_INFO(font_fallbacks))
16516           {
16517              /* Already stringshared here, unref */
16518              eina_stringshare_del(nfont_fallbacks);
16519           }
16520         else
16521           {
16522              // Set immediately, load font_fallbacks later
16523              _FMT_INFO(font_fallbacks) = nfont_fallbacks;
16524              _canvas_text_format_changed(obj, o);
16525           }
16526      }
16527 }
16528 
16529 static const char*
_efl_canvas_textblock_efl_text_font_properties_font_fallbacks_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)16530 _efl_canvas_textblock_efl_text_font_properties_font_fallbacks_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
16531 {
16532    return _FMT_INFO(font_fallbacks);
16533 }
16534 
16535 static void
_efl_canvas_textblock_efl_text_font_properties_font_lang_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,const char * font_lang EINA_UNUSED)16536 _efl_canvas_textblock_efl_text_font_properties_font_lang_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, const char *font_lang EINA_UNUSED)
16537 {
16538    if (_FMT_INFO(font_lang) != font_lang)
16539      {
16540         if (eina_stringshare_replace(&_FMT_INFO(font_lang), font_lang))
16541           {
16542              _canvas_text_format_changed(obj, o);
16543           }
16544      }
16545 }
16546 
16547 static const char*
_efl_canvas_textblock_efl_text_font_properties_font_lang_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)16548 _efl_canvas_textblock_efl_text_font_properties_font_lang_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
16549 {
16550    return _FMT_INFO(font_lang);
16551 }
16552 
16553 static void
_efl_canvas_textblock_efl_text_font_properties_font_weight_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,Efl_Text_Font_Weight font_weight EINA_UNUSED)16554 _efl_canvas_textblock_efl_text_font_properties_font_weight_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, Efl_Text_Font_Weight font_weight EINA_UNUSED)
16555 {
16556    if (_FMT_INFO(font_weight) == font_weight) return;
16557    _FMT_INFO(font_weight) = font_weight;
16558    _canvas_text_format_changed(obj, o);
16559 }
16560 
16561 static Efl_Text_Font_Weight
_efl_canvas_textblock_efl_text_font_properties_font_weight_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)16562 _efl_canvas_textblock_efl_text_font_properties_font_weight_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
16563 {
16564    return _FMT_INFO(font_weight);
16565 }
16566 
16567 static void
_efl_canvas_textblock_efl_text_font_properties_font_slant_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,Efl_Text_Font_Slant font_slant EINA_UNUSED)16568 _efl_canvas_textblock_efl_text_font_properties_font_slant_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, Efl_Text_Font_Slant font_slant EINA_UNUSED)
16569 {
16570    ASYNC_BLOCK;
16571    if (_FMT_INFO(font_slant) == font_slant) return;
16572    _FMT_INFO(font_slant) = font_slant;
16573    _canvas_text_format_changed(obj, o);
16574 }
16575 
16576 static Efl_Text_Font_Slant
_efl_canvas_textblock_efl_text_font_properties_font_slant_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)16577 _efl_canvas_textblock_efl_text_font_properties_font_slant_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
16578 {
16579    return _FMT_INFO(font_slant);
16580 }
16581 
16582 static void
_efl_canvas_textblock_efl_text_font_properties_font_width_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,Efl_Text_Font_Width font_width EINA_UNUSED)16583 _efl_canvas_textblock_efl_text_font_properties_font_width_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, Efl_Text_Font_Width font_width EINA_UNUSED)
16584 {
16585    ASYNC_BLOCK;
16586    if (_FMT_INFO(font_width) == font_width) return;
16587    _FMT_INFO(font_width) = font_width;
16588    _canvas_text_format_changed(obj, o);
16589 }
16590 
16591 static Efl_Text_Font_Width
_efl_canvas_textblock_efl_text_font_properties_font_width_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)16592 _efl_canvas_textblock_efl_text_font_properties_font_width_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
16593 {
16594    return _FMT_INFO(font_width);
16595 }
16596 
16597 EOLIAN static void
_efl_canvas_textblock_efl_text_font_properties_font_bitmap_scalable_set(Eo * obj,Efl_Canvas_Textblock_Data * o,Efl_Text_Font_Bitmap_Scalable bitmap_scalable)16598 _efl_canvas_textblock_efl_text_font_properties_font_bitmap_scalable_set(Eo *obj, Efl_Canvas_Textblock_Data *o, Efl_Text_Font_Bitmap_Scalable bitmap_scalable)
16599 {
16600    if (_FMT_INFO(bitmap_scalable) == bitmap_scalable) return;
16601    _FMT_INFO(bitmap_scalable) = bitmap_scalable;
16602    _canvas_text_format_changed(obj, o);
16603 }
16604 
16605 EOLIAN static Efl_Text_Font_Bitmap_Scalable
_efl_canvas_textblock_efl_text_font_properties_font_bitmap_scalable_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o)16606 _efl_canvas_textblock_efl_text_font_properties_font_bitmap_scalable_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o)
16607 {
16608    return _FMT_INFO(bitmap_scalable);
16609 }
16610 
16611 /* Efl.Text.Style interface implementation */
16612 
16613 /* Helper: sets color fields of style 'x' and informs if any are changed. */
16614 #define _FMT_COLOR_SET(x) \
16615    if ((_FMT(color.x).r == r) && (_FMT(color.x).g == g) \
16616       && (_FMT(color.x).b == b) &&  (_FMT(color.x).a == a)) return; \
16617       _FMT(color.x).r = r; \
16618       _FMT(color.x).g = g; \
16619       _FMT(color.x).b = b; \
16620       _FMT(color.x).a = a; \
16621       _canvas_text_format_changed(obj, o);
16622 
16623 /* Helper: returns color fields of style 'x'. */
16624 #define _FMT_COLOR_RET(x) \
16625    if (r) *r = _FMT(color.x).r; \
16626    if (g) *g = _FMT(color.x).g; \
16627    if (b) *b = _FMT(color.x).b; \
16628    if (a) *a = _FMT(color.x).a;
16629 
16630 /* Helper: updates format field, and informs if changed. */
16631 #define _FMT_SET(x, v) \
16632    if (_FMT(x) == v) return; \
16633    _FMT(x) = v; \
16634    _canvas_text_format_changed(obj, o);
16635 #define _FMT_SETD(x, v) \
16636    if (EINA_DBL_EQ(_FMT(x), v)) return; \
16637    _FMT(x) = v; \
16638    _canvas_text_format_changed(obj, o);
16639 
16640 /* Helper: updates format field of extended format information, and informs if changed. */
16641 #define _FMT_INFO_SET_START(x, v) \
16642    Eina_Bool changed = EINA_FALSE; \
16643    if (_FMT_INFO(x) == v) return; \
16644    changed = EINA_TRUE; \
16645    _FMT_INFO(x) = v; \
16646 
16647 #define _FMT_INFO_SET_END() \
16648    if (changed) _canvas_text_format_changed(obj, o);
16649 
16650 static void
_efl_canvas_textblock_efl_text_style_text_color_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char r EINA_UNUSED,unsigned char g EINA_UNUSED,unsigned char b EINA_UNUSED,unsigned char a EINA_UNUSED)16651 _efl_canvas_textblock_efl_text_style_text_color_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char r EINA_UNUSED, unsigned char g EINA_UNUSED, unsigned char b EINA_UNUSED, unsigned char a EINA_UNUSED)
16652 {
16653    ASYNC_BLOCK;
16654    _FMT_COLOR_SET(normal);
16655 }
16656 
16657 static void
_efl_canvas_textblock_efl_text_style_text_color_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char * r EINA_UNUSED,unsigned char * g EINA_UNUSED,unsigned char * b EINA_UNUSED,unsigned char * a EINA_UNUSED)16658 _efl_canvas_textblock_efl_text_style_text_color_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char *r EINA_UNUSED, unsigned char *g EINA_UNUSED, unsigned char *b EINA_UNUSED, unsigned char *a EINA_UNUSED)
16659 {
16660    _FMT_COLOR_RET(normal);
16661 }
16662 
16663 static void
_efl_canvas_textblock_efl_text_style_text_background_type_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,Efl_Text_Style_Background_Type type EINA_UNUSED)16664 _efl_canvas_textblock_efl_text_style_text_background_type_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, Efl_Text_Style_Background_Type type EINA_UNUSED)
16665 {
16666    ASYNC_BLOCK;
16667    _FMT_SET(backing, type);
16668 }
16669 
16670 static Efl_Text_Style_Background_Type
_efl_canvas_textblock_efl_text_style_text_background_type_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)16671 _efl_canvas_textblock_efl_text_style_text_background_type_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
16672 {
16673    return _FMT(backing);
16674 }
16675 
16676 static void
_efl_canvas_textblock_efl_text_style_text_background_color_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char r EINA_UNUSED,unsigned char g EINA_UNUSED,unsigned char b EINA_UNUSED,unsigned char a EINA_UNUSED)16677 _efl_canvas_textblock_efl_text_style_text_background_color_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char r EINA_UNUSED, unsigned char g EINA_UNUSED, unsigned char b EINA_UNUSED, unsigned char a EINA_UNUSED)
16678 {
16679    ASYNC_BLOCK;
16680    _FMT_COLOR_SET(backing);
16681 }
16682 
16683 static void
_efl_canvas_textblock_efl_text_style_text_background_color_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char * r EINA_UNUSED,unsigned char * g EINA_UNUSED,unsigned char * b EINA_UNUSED,unsigned char * a EINA_UNUSED)16684 _efl_canvas_textblock_efl_text_style_text_background_color_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char *r EINA_UNUSED, unsigned char *g EINA_UNUSED, unsigned char *b EINA_UNUSED, unsigned char *a EINA_UNUSED)
16685 {
16686    _FMT_COLOR_RET(backing);
16687 }
16688 
16689 static struct
16690 {
16691    Eina_Bool underline_single : 1;
16692    Eina_Bool underline_double : 1;
16693    Eina_Bool underline_dashed : 1;
16694 } _style_underline_map[] = {
16695      { 0, 0, 0 },
16696      { 1, 0, 0 },
16697      { 1, 1, 0 },
16698      { 0, 0, 1 }
16699 };
16700 
16701 static void
_efl_canvas_textblock_efl_text_style_text_underline_type_set(Eo * obj,Efl_Canvas_Textblock_Data * o,Efl_Text_Style_Underline_Type type)16702 _efl_canvas_textblock_efl_text_style_text_underline_type_set(Eo *obj, Efl_Canvas_Textblock_Data *o, Efl_Text_Style_Underline_Type type)
16703 {
16704    if (efl_text_underline_type_get(obj) == type)
16705      return;
16706 
16707    ASYNC_BLOCK;
16708    _FMT(underline) = _style_underline_map[type].underline_single;
16709    _FMT(underline2) = _style_underline_map[type].underline_double;
16710    _FMT(underline_dash) = _style_underline_map[type].underline_dashed;
16711    _canvas_text_format_changed(obj, o);
16712 }
16713 
16714 static Efl_Text_Style_Underline_Type
_efl_canvas_textblock_efl_text_style_text_underline_type_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o)16715 _efl_canvas_textblock_efl_text_style_text_underline_type_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o)
16716 {
16717    if(_FMT(underline_dash)) return EFL_TEXT_STYLE_UNDERLINE_TYPE_DASHED;
16718    else if (_FMT(underline2)) return EFL_TEXT_STYLE_UNDERLINE_TYPE_DOUBLE;
16719    else if (_FMT(underline)) return EFL_TEXT_STYLE_UNDERLINE_TYPE_SINGLE;
16720    else return EFL_TEXT_STYLE_UNDERLINE_TYPE_NONE;
16721 }
16722 
16723 static void
_efl_canvas_textblock_efl_text_style_text_underline_color_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char r EINA_UNUSED,unsigned char g EINA_UNUSED,unsigned char b EINA_UNUSED,unsigned char a EINA_UNUSED)16724 _efl_canvas_textblock_efl_text_style_text_underline_color_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char r EINA_UNUSED, unsigned char g EINA_UNUSED, unsigned char b EINA_UNUSED, unsigned char a EINA_UNUSED)
16725 {
16726    ASYNC_BLOCK;
16727    _FMT_COLOR_SET(underline);
16728 }
16729 
16730 static void
_efl_canvas_textblock_efl_text_style_text_underline_color_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char * r EINA_UNUSED,unsigned char * g EINA_UNUSED,unsigned char * b EINA_UNUSED,unsigned char * a EINA_UNUSED)16731 _efl_canvas_textblock_efl_text_style_text_underline_color_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char *r EINA_UNUSED, unsigned char *g EINA_UNUSED, unsigned char *b EINA_UNUSED, unsigned char *a EINA_UNUSED)
16732 {
16733    _FMT_COLOR_RET(underline);
16734 }
16735 
16736 static void
_efl_canvas_textblock_efl_text_style_text_underline_height_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,double height EINA_UNUSED)16737 _efl_canvas_textblock_efl_text_style_text_underline_height_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, double height EINA_UNUSED)
16738 {
16739    ASYNC_BLOCK;
16740    _FMT_SETD(underline_height, height);
16741 }
16742 
16743 static double
_efl_canvas_textblock_efl_text_style_text_underline_height_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)16744 _efl_canvas_textblock_efl_text_style_text_underline_height_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
16745 {
16746    return _FMT(underline_height);
16747 }
16748 
16749 static void
_efl_canvas_textblock_efl_text_style_text_underline_dashed_color_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char r EINA_UNUSED,unsigned char g EINA_UNUSED,unsigned char b EINA_UNUSED,unsigned char a EINA_UNUSED)16750 _efl_canvas_textblock_efl_text_style_text_underline_dashed_color_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char r EINA_UNUSED, unsigned char g EINA_UNUSED, unsigned char b EINA_UNUSED, unsigned char a EINA_UNUSED)
16751 {
16752    ASYNC_BLOCK;
16753    _FMT_COLOR_SET(underline_dash);
16754 }
16755 
16756 static void
_efl_canvas_textblock_efl_text_style_text_underline_dashed_color_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char * r EINA_UNUSED,unsigned char * g EINA_UNUSED,unsigned char * b EINA_UNUSED,unsigned char * a EINA_UNUSED)16757 _efl_canvas_textblock_efl_text_style_text_underline_dashed_color_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char *r EINA_UNUSED, unsigned char *g EINA_UNUSED, unsigned char *b EINA_UNUSED, unsigned char *a EINA_UNUSED)
16758 {
16759    _FMT_COLOR_RET(underline_dash);
16760 }
16761 
16762 static void
_efl_canvas_textblock_efl_text_style_text_underline_dashed_width_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,int width EINA_UNUSED)16763 _efl_canvas_textblock_efl_text_style_text_underline_dashed_width_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, int width EINA_UNUSED)
16764 {
16765    ASYNC_BLOCK;
16766    _FMT_SET(underline_dash_width, width);
16767 }
16768 
16769 static int
_efl_canvas_textblock_efl_text_style_text_underline_dashed_width_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)16770 _efl_canvas_textblock_efl_text_style_text_underline_dashed_width_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
16771 {
16772    return _FMT(underline_dash_width);
16773 }
16774 
16775 static void
_efl_canvas_textblock_efl_text_style_text_underline_dashed_gap_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,int gap EINA_UNUSED)16776 _efl_canvas_textblock_efl_text_style_text_underline_dashed_gap_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, int gap EINA_UNUSED)
16777 {
16778    ASYNC_BLOCK;
16779    _FMT_SET(underline_dash_gap, gap);
16780 }
16781 
16782 static int
_efl_canvas_textblock_efl_text_style_text_underline_dashed_gap_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)16783 _efl_canvas_textblock_efl_text_style_text_underline_dashed_gap_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
16784 {
16785    return _FMT(underline_dash_gap);
16786 }
16787 
16788 static void
_efl_canvas_textblock_efl_text_style_text_secondary_underline_color_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char r EINA_UNUSED,unsigned char g EINA_UNUSED,unsigned char b EINA_UNUSED,unsigned char a EINA_UNUSED)16789 _efl_canvas_textblock_efl_text_style_text_secondary_underline_color_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char r EINA_UNUSED, unsigned char g EINA_UNUSED, unsigned char b EINA_UNUSED, unsigned char a EINA_UNUSED)
16790 {
16791    ASYNC_BLOCK;
16792    _FMT_COLOR_SET(underline2);
16793 }
16794 
16795 static void
_efl_canvas_textblock_efl_text_style_text_secondary_underline_color_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char * r EINA_UNUSED,unsigned char * g EINA_UNUSED,unsigned char * b EINA_UNUSED,unsigned char * a EINA_UNUSED)16796 _efl_canvas_textblock_efl_text_style_text_secondary_underline_color_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char *r EINA_UNUSED, unsigned char *g EINA_UNUSED, unsigned char *b EINA_UNUSED, unsigned char *a EINA_UNUSED)
16797 {
16798    _FMT_COLOR_RET(underline2);
16799 }
16800 
16801 static void
_efl_canvas_textblock_efl_text_style_text_strikethrough_type_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,Efl_Text_Style_Strikethrough_Type type EINA_UNUSED)16802 _efl_canvas_textblock_efl_text_style_text_strikethrough_type_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, Efl_Text_Style_Strikethrough_Type type EINA_UNUSED)
16803 {
16804    ASYNC_BLOCK;
16805    _FMT_SET(strikethrough, type);
16806 }
16807 
16808 static Efl_Text_Style_Strikethrough_Type
_efl_canvas_textblock_efl_text_style_text_strikethrough_type_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)16809 _efl_canvas_textblock_efl_text_style_text_strikethrough_type_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
16810 {
16811    return _FMT(strikethrough);
16812 }
16813 
16814 static void
_efl_canvas_textblock_efl_text_style_text_strikethrough_color_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char r EINA_UNUSED,unsigned char g EINA_UNUSED,unsigned char b EINA_UNUSED,unsigned char a EINA_UNUSED)16815 _efl_canvas_textblock_efl_text_style_text_strikethrough_color_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char r EINA_UNUSED, unsigned char g EINA_UNUSED, unsigned char b EINA_UNUSED, unsigned char a EINA_UNUSED)
16816 {
16817    ASYNC_BLOCK;
16818    _FMT_COLOR_SET(strikethrough);
16819 }
16820 
16821 static void
_efl_canvas_textblock_efl_text_style_text_strikethrough_color_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char * r EINA_UNUSED,unsigned char * g EINA_UNUSED,unsigned char * b EINA_UNUSED,unsigned char * a EINA_UNUSED)16822 _efl_canvas_textblock_efl_text_style_text_strikethrough_color_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char *r EINA_UNUSED, unsigned char *g EINA_UNUSED, unsigned char *b EINA_UNUSED, unsigned char *a EINA_UNUSED)
16823 {
16824    _FMT_COLOR_RET(strikethrough);
16825 }
16826 
16827 static const struct
16828 {
16829    Efl_Text_Style_Effect_Type x;
16830    Evas_Text_Style_Type y;
16831 } _map_style_effect[] = {
16832    { EFL_TEXT_STYLE_EFFECT_TYPE_NONE,  EVAS_TEXT_STYLE_PLAIN },
16833    { EFL_TEXT_STYLE_EFFECT_TYPE_SHADOW, EVAS_TEXT_STYLE_SHADOW },
16834    { EFL_TEXT_STYLE_EFFECT_TYPE_OUTLINE, EVAS_TEXT_STYLE_OUTLINE },
16835    { EFL_TEXT_STYLE_EFFECT_TYPE_SOFT_OUTLINE, EVAS_TEXT_STYLE_SOFT_OUTLINE },
16836    { EFL_TEXT_STYLE_EFFECT_TYPE_OUTLINE_SHADOW, EVAS_TEXT_STYLE_OUTLINE_SHADOW },
16837    { EFL_TEXT_STYLE_EFFECT_TYPE_OUTLINE_SOFT_SHADOW, EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW },
16838    { EFL_TEXT_STYLE_EFFECT_TYPE_GLOW, EVAS_TEXT_STYLE_GLOW },
16839    { EFL_TEXT_STYLE_EFFECT_TYPE_FAR_SHADOW, EVAS_TEXT_STYLE_FAR_SHADOW },
16840    { EFL_TEXT_STYLE_EFFECT_TYPE_SOFT_SHADOW, EVAS_TEXT_STYLE_SOFT_SHADOW },
16841    { EFL_TEXT_STYLE_EFFECT_TYPE_FAR_SOFT_SHADOW, EVAS_TEXT_STYLE_FAR_SOFT_SHADOW },
16842 };
16843 
16844 static const struct
16845 {
16846    Efl_Text_Style_Shadow_Direction x;
16847    Evas_Text_Style_Type y;
16848 } _map_shadow_dir[] = {
16849    { EFL_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_RIGHT },
16850    { EFL_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM },
16851    { EFL_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT, EVAS_TEXT_STYLE_SHADOW_DIRECTION_BOTTOM_LEFT },
16852    { EFL_TEXT_STYLE_SHADOW_DIRECTION_LEFT, EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT },
16853    { EFL_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT, EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_LEFT },
16854    { EFL_TEXT_STYLE_SHADOW_DIRECTION_TOP, EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP },
16855    { EFL_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT, EVAS_TEXT_STYLE_SHADOW_DIRECTION_TOP_RIGHT },
16856    { EFL_TEXT_STYLE_SHADOW_DIRECTION_RIGHT, EVAS_TEXT_STYLE_SHADOW_DIRECTION_RIGHT },
16857 };
16858 
16859 #define MAP_LEN(a) ((sizeof (a)) / sizeof((a)[0]))
16860 
16861 static Evas_Text_Style_Type
_get_style_from_map(Efl_Text_Style_Effect_Type st)16862 _get_style_from_map(Efl_Text_Style_Effect_Type st)
16863 {
16864    size_t i;
16865    size_t len = MAP_LEN(_map_style_effect);
16866    for (i = 0; i < len; i++)
16867      {
16868         if (_map_style_effect[i].x == st)
16869            return _map_style_effect[i].y;
16870      }
16871    ERR("Mapping style failed. Please check code\n");
16872    return EVAS_TEXT_STYLE_SHADOW; // shouldn't reach
16873 }
16874 
16875 static Evas_Text_Style_Type
_get_dir_from_map(Efl_Text_Style_Shadow_Direction dir)16876 _get_dir_from_map(Efl_Text_Style_Shadow_Direction dir)
16877 {
16878    size_t i;
16879    size_t len = MAP_LEN(_map_shadow_dir);
16880    for (i = 0; i < len; i++)
16881      {
16882         if (_map_shadow_dir[i].x == dir)
16883            return _map_shadow_dir[i].y;
16884      }
16885    ERR("Mapping direction failed. Please check code\n");
16886    return EVAS_TEXT_STYLE_SHADOW_DIRECTION_LEFT; // shouldn't reach
16887 }
16888 
16889 static void
_efl_canvas_textblock_efl_text_style_text_effect_type_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,Efl_Text_Style_Effect_Type type EINA_UNUSED)16890 _efl_canvas_textblock_efl_text_style_text_effect_type_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, Efl_Text_Style_Effect_Type type EINA_UNUSED)
16891 {
16892    ASYNC_BLOCK;
16893    _FMT_INFO_SET_START(effect, type);
16894    _FMT(style) = _get_style_from_map(type);
16895    // Re-apply shadow direction
16896    EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(_FMT(style),
16897          _get_dir_from_map(_FMT_INFO(shadow_direction)));
16898    _FMT_INFO_SET_END();
16899 }
16900 
16901 static Efl_Text_Style_Effect_Type
_efl_canvas_textblock_efl_text_style_text_effect_type_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)16902 _efl_canvas_textblock_efl_text_style_text_effect_type_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
16903 {
16904    return _FMT_INFO(effect);
16905 }
16906 
16907 static void
_efl_canvas_textblock_efl_text_style_text_outline_color_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char r EINA_UNUSED,unsigned char g EINA_UNUSED,unsigned char b EINA_UNUSED,unsigned char a EINA_UNUSED)16908 _efl_canvas_textblock_efl_text_style_text_outline_color_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char r EINA_UNUSED, unsigned char g EINA_UNUSED, unsigned char b EINA_UNUSED, unsigned char a EINA_UNUSED)
16909 {
16910    ASYNC_BLOCK;
16911    _FMT_COLOR_SET(outline);
16912 }
16913 
16914 static void
_efl_canvas_textblock_efl_text_style_text_outline_color_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char * r EINA_UNUSED,unsigned char * g EINA_UNUSED,unsigned char * b EINA_UNUSED,unsigned char * a EINA_UNUSED)16915 _efl_canvas_textblock_efl_text_style_text_outline_color_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char *r EINA_UNUSED, unsigned char *g EINA_UNUSED, unsigned char *b EINA_UNUSED, unsigned char *a EINA_UNUSED)
16916 {
16917    _FMT_COLOR_RET(outline);
16918 }
16919 
16920 static void
_efl_canvas_textblock_efl_text_style_text_shadow_direction_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,Efl_Text_Style_Shadow_Direction type EINA_UNUSED)16921 _efl_canvas_textblock_efl_text_style_text_shadow_direction_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, Efl_Text_Style_Shadow_Direction type EINA_UNUSED)
16922 {
16923    ASYNC_BLOCK;
16924    _FMT_INFO_SET_START(shadow_direction, type);
16925    EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(_FMT(style),
16926          _get_dir_from_map(_FMT_INFO(shadow_direction)));
16927    _FMT_INFO_SET_END();
16928 }
16929 
16930 static Efl_Text_Style_Shadow_Direction
_efl_canvas_textblock_efl_text_style_text_shadow_direction_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)16931 _efl_canvas_textblock_efl_text_style_text_shadow_direction_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
16932 {
16933    return _FMT_INFO(shadow_direction);
16934 }
16935 
16936 static void
_efl_canvas_textblock_efl_text_style_text_shadow_color_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char r EINA_UNUSED,unsigned char g EINA_UNUSED,unsigned char b EINA_UNUSED,unsigned char a EINA_UNUSED)16937 _efl_canvas_textblock_efl_text_style_text_shadow_color_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char r EINA_UNUSED, unsigned char g EINA_UNUSED, unsigned char b EINA_UNUSED, unsigned char a EINA_UNUSED)
16938 {
16939    ASYNC_BLOCK;
16940    _FMT_COLOR_SET(shadow);
16941 }
16942 
16943 static void
_efl_canvas_textblock_efl_text_style_text_shadow_color_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char * r EINA_UNUSED,unsigned char * g EINA_UNUSED,unsigned char * b EINA_UNUSED,unsigned char * a EINA_UNUSED)16944 _efl_canvas_textblock_efl_text_style_text_shadow_color_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char *r EINA_UNUSED, unsigned char *g EINA_UNUSED, unsigned char *b EINA_UNUSED, unsigned char *a EINA_UNUSED)
16945 {
16946    _FMT_COLOR_RET(shadow);
16947 }
16948 
16949 static void
_efl_canvas_textblock_efl_text_style_text_glow_color_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char r EINA_UNUSED,unsigned char g EINA_UNUSED,unsigned char b EINA_UNUSED,unsigned char a EINA_UNUSED)16950 _efl_canvas_textblock_efl_text_style_text_glow_color_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char r EINA_UNUSED, unsigned char g EINA_UNUSED, unsigned char b EINA_UNUSED, unsigned char a EINA_UNUSED)
16951 {
16952    ASYNC_BLOCK;
16953    _FMT_COLOR_SET(glow);
16954 }
16955 
16956 static void
_efl_canvas_textblock_efl_text_style_text_glow_color_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char * r EINA_UNUSED,unsigned char * g EINA_UNUSED,unsigned char * b EINA_UNUSED,unsigned char * a EINA_UNUSED)16957 _efl_canvas_textblock_efl_text_style_text_glow_color_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char *r EINA_UNUSED, unsigned char *g EINA_UNUSED, unsigned char *b EINA_UNUSED, unsigned char *a EINA_UNUSED)
16958 {
16959    _FMT_COLOR_RET(glow);
16960 }
16961 
16962 static void
_efl_canvas_textblock_efl_text_style_text_secondary_glow_color_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char r EINA_UNUSED,unsigned char g EINA_UNUSED,unsigned char b EINA_UNUSED,unsigned char a EINA_UNUSED)16963 _efl_canvas_textblock_efl_text_style_text_secondary_glow_color_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char r EINA_UNUSED, unsigned char g EINA_UNUSED, unsigned char b EINA_UNUSED, unsigned char a EINA_UNUSED)
16964 {
16965    ASYNC_BLOCK;
16966    _FMT_COLOR_SET(glow2);
16967 }
16968 
16969 static void
_efl_canvas_textblock_efl_text_style_text_secondary_glow_color_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,unsigned char * r EINA_UNUSED,unsigned char * g EINA_UNUSED,unsigned char * b EINA_UNUSED,unsigned char * a EINA_UNUSED)16970 _efl_canvas_textblock_efl_text_style_text_secondary_glow_color_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, unsigned char *r EINA_UNUSED, unsigned char *g EINA_UNUSED, unsigned char *b EINA_UNUSED, unsigned char *a EINA_UNUSED)
16971 {
16972    _FMT_COLOR_RET(glow2);
16973 }
16974 
16975 static void
_efl_canvas_textblock_efl_text_style_text_gfx_filter_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,const char * gfx_filter_name)16976 _efl_canvas_textblock_efl_text_style_text_gfx_filter_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED,
16977       const char *gfx_filter_name)
16978 {
16979    ASYNC_BLOCK;
16980    Eina_Stringshare *ngfx_filter_name;
16981 
16982    if (_FMT_INFO(gfx_filter_name) != gfx_filter_name)
16983      {
16984         ngfx_filter_name = eina_stringshare_add(gfx_filter_name);
16985         if (_FMT_INFO(gfx_filter_name) == ngfx_filter_name)
16986           {
16987              /* Already stringshared here, unref */
16988              eina_stringshare_del(ngfx_filter_name);
16989           }
16990         else
16991           {
16992              // Set immediately, load style_gfx_filter_name later
16993              _FMT_INFO(gfx_filter_name) = ngfx_filter_name;
16994              _canvas_text_format_changed(obj, o);
16995           }
16996      }
16997 }
16998 
16999 static const char *
_efl_canvas_textblock_efl_text_style_text_gfx_filter_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)17000 _efl_canvas_textblock_efl_text_style_text_gfx_filter_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
17001 {
17002    return _FMT_INFO(gfx_filter_name)?_FMT_INFO(gfx_filter_name):NULL;
17003 }
17004 
17005 static void
_efl_canvas_textblock_efl_text_format_ellipsis_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,double value EINA_UNUSED)17006 _efl_canvas_textblock_efl_text_format_ellipsis_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, double value EINA_UNUSED)
17007 {
17008    ASYNC_BLOCK;
17009    _FMT_SETD(ellipsis, value);
17010 }
17011 
17012 static double
_efl_canvas_textblock_efl_text_format_ellipsis_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)17013 _efl_canvas_textblock_efl_text_format_ellipsis_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
17014 {
17015    return _FMT(ellipsis);
17016 }
17017 
17018 static void
_efl_canvas_textblock_efl_text_format_wrap_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,Efl_Text_Format_Wrap wrap EINA_UNUSED)17019 _efl_canvas_textblock_efl_text_format_wrap_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, Efl_Text_Format_Wrap wrap EINA_UNUSED)
17020 {
17021    ASYNC_BLOCK;
17022    _FMT_INFO_SET_START(wrap, wrap);
17023    _FMT(wrap_word) = (wrap == EFL_TEXT_FORMAT_WRAP_WORD);
17024    _FMT(wrap_char) = (wrap == EFL_TEXT_FORMAT_WRAP_CHAR);
17025    _FMT(wrap_mixed) = (wrap == EFL_TEXT_FORMAT_WRAP_MIXED);
17026    _FMT(wrap_hyphenation) = (wrap == EFL_TEXT_FORMAT_WRAP_HYPHENATION);
17027    _FMT_INFO_SET_END();
17028 }
17029 
17030 static Efl_Text_Format_Wrap
_efl_canvas_textblock_efl_text_format_wrap_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)17031 _efl_canvas_textblock_efl_text_format_wrap_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
17032 {
17033    return _FMT_INFO(wrap);
17034 }
17035 
17036 void
clean_cursors_at_node(Eina_List ** all_cursors,Evas_Object_Textblock_Node_Text * tn,Evas_Object_Textblock_Node_Text * main_node,unsigned int len)17037 clean_cursors_at_node(Eina_List **all_cursors,
17038                       Evas_Object_Textblock_Node_Text *tn,
17039                       Evas_Object_Textblock_Node_Text *main_node,
17040                       unsigned int len)
17041 {
17042    Eina_List *l, *ll;
17043    Efl_Text_Cursor_Handle *itr_cursor;
17044    EINA_LIST_FOREACH_SAFE (*all_cursors, l, ll, itr_cursor)
17045      {
17046         if (tn == itr_cursor->node)
17047           {
17048              itr_cursor->pos += len;
17049              itr_cursor->node = main_node;
17050              itr_cursor->changed = EINA_TRUE;
17051              *all_cursors = eina_list_remove(*all_cursors, itr_cursor);
17052           }
17053      }
17054 }
17055 
17056 /**
17057  * @internal
17058  * Combine all text nodes in a single node, for convert from multi-line to single-line.
17059  * @param obj The evas object, must not be NULL.
17060  * @return EINA_TRUE if text nodes merged, else return EINA_FALSE
17061  */
17062 static void
_merge_to_first_text_nodes(const Evas_Object * eo_obj)17063 _merge_to_first_text_nodes(const Evas_Object *eo_obj)
17064 {
17065    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
17066    Evas_Object_Textblock_Node_Text *main_node, *tn;
17067    Evas_Object_Textblock_Node_Format  *fn;
17068    int len, temp_len;
17069 
17070    if (!o->text_nodes || !(EINA_INLIST_GET(o->text_nodes)->next))
17071      return;
17072 
17073    main_node = o->text_nodes;
17074    main_node->dirty = EINA_TRUE;
17075    len = (int) eina_ustrbuf_length_get(main_node->unicode);
17076 
17077    Eina_List *all_cursors;
17078    all_cursors = eina_list_clone(o->cursors);
17079    all_cursors = eina_list_append(all_cursors, o->cursor);
17080 
17081    while (o->text_nodes && (tn = _NODE_TEXT(EINA_INLIST_GET(o->text_nodes)->next)))
17082      {
17083         fn = tn->format_node;
17084 
17085         if (fn && (fn->text_node == tn))
17086           {
17087              fn->offset++; //add prev ps
17088           }
17089 
17090         while (fn && (fn->text_node == tn))
17091           {
17092              fn->text_node = main_node;
17093              fn->format_change = EINA_TRUE;
17094 
17095              fn = _NODE_FORMAT(EINA_INLIST_GET(fn)->next);
17096           }
17097 
17098         temp_len = (int) eina_ustrbuf_length_get(tn->unicode);
17099         eina_ustrbuf_append_length(main_node->unicode, eina_ustrbuf_string_get(tn->unicode), temp_len);
17100 
17101         clean_cursors_at_node(&all_cursors, tn, main_node, len);
17102 
17103         len += temp_len;
17104 
17105         o->text_nodes = _NODE_TEXT(eina_inlist_remove(
17106                  EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(tn)));
17107         _evas_textblock_node_text_free(tn);
17108      }
17109 
17110    eina_list_free(all_cursors);
17111 }
17112 
17113 void
clean_cursors_in_range(Eina_List ** all_cursors,Evas_Object_Textblock_Node_Text * tn,unsigned int start,unsigned int end)17114 clean_cursors_in_range(Eina_List **all_cursors, Evas_Object_Textblock_Node_Text *tn, unsigned int start, unsigned int end)
17115 {
17116    Eina_List *l, *ll;
17117    Efl_Text_Cursor_Handle *itr_cursor;
17118    EINA_LIST_FOREACH_SAFE (*all_cursors, l, ll, itr_cursor)
17119      {
17120         if (itr_cursor->pos >= start && itr_cursor->pos <= end)
17121           {
17122               itr_cursor->pos -= start;
17123               itr_cursor->node = tn;
17124               itr_cursor->changed = EINA_TRUE;
17125               *all_cursors = eina_list_remove(*all_cursors, itr_cursor);
17126           }
17127      }
17128 }
17129 
17130 /**
17131  * @internal
17132  * split text node into multiple text nodes based on ps, called for convert from singleline to multiline.
17133  * @param obj The evas object, must not be NULL.
17134  * @return EINA_TRUE if text nodes splitted, else return EINA_FALSE
17135  */
17136 static void
_split_text_nodes(const Evas_Object * eo_obj)17137 _split_text_nodes(const Evas_Object *eo_obj)
17138 {
17139    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
17140    Evas_Object_Textblock_Node_Text *tn;
17141    Evas_Object_Textblock_Node_Format  *fn;
17142    Eina_Unicode *all_unicode;
17143    Eina_List *all_cursors;
17144    unsigned int len = 0, str_start = 0;
17145 
17146    if (!o->text_nodes || !o->format_nodes)
17147      return;
17148 
17149    tn = o->text_nodes;
17150    fn = tn->format_node;
17151 
17152    while (fn && !_IS_PARAGRAPH_SEPARATOR_SIMPLE(fn->format))
17153      {
17154         len += fn->offset;
17155         fn = _NODE_FORMAT(EINA_INLIST_GET(fn)->next);
17156      }
17157 
17158    if(!fn) return;
17159 
17160    tn->dirty = EINA_TRUE;
17161    len += fn->offset + 1;
17162    all_unicode = eina_ustrbuf_string_steal(tn->unicode);
17163 
17164    eina_ustrbuf_append_n(tn->unicode, all_unicode, len);
17165 
17166    str_start += len;
17167 
17168    tn = _evas_textblock_node_text_new();
17169    o->text_nodes = _NODE_TEXT(eina_inlist_append(
17170                      EINA_INLIST_GET(o->text_nodes),
17171                      EINA_INLIST_GET(tn)));
17172 
17173    fn = _NODE_FORMAT(EINA_INLIST_GET(fn)->next);
17174 
17175    all_cursors = eina_list_clone(o->cursors);
17176    all_cursors = eina_list_append(all_cursors, o->cursor);
17177 
17178    while (fn)
17179      {
17180         len = 0;
17181         tn->format_node = fn;
17182         tn->format_node->offset--;
17183 
17184         while (fn && !_IS_PARAGRAPH_SEPARATOR_SIMPLE(fn->format))
17185           {
17186              len += fn->offset;
17187              fn->text_node = tn;
17188              fn->format_change = EINA_TRUE;
17189              fn = _NODE_FORMAT(EINA_INLIST_GET(fn)->next);
17190           }
17191 
17192         if (!fn) break;
17193 
17194         fn->text_node = tn;
17195         fn->format_change = EINA_TRUE;
17196         len += fn->offset + 1;
17197         eina_ustrbuf_append_n(tn->unicode, all_unicode + str_start, len);
17198 
17199         clean_cursors_in_range(&all_cursors, tn, str_start, str_start + len);
17200 
17201         str_start += len;
17202 
17203         tn = _evas_textblock_node_text_new();
17204         o->text_nodes = _NODE_TEXT(eina_inlist_append(
17205                           EINA_INLIST_GET(o->text_nodes),
17206                           EINA_INLIST_GET(tn)));
17207 
17208         fn = _NODE_FORMAT(EINA_INLIST_GET(fn)->next);
17209      }
17210 
17211    if (!tn->format_node)
17212      tn->format_node = _NODE_FORMAT(EINA_INLIST_GET(o->format_nodes)->last);
17213 
17214    len = eina_unicode_strlen(all_unicode + str_start);
17215    eina_ustrbuf_append_n(tn->unicode, all_unicode + str_start, len);
17216 
17217    clean_cursors_in_range(&all_cursors, tn, str_start, str_start + len);
17218    eina_list_free(all_cursors);
17219 
17220    free(all_unicode);
17221 }
17222 
17223 static void
_efl_canvas_textblock_efl_text_format_multiline_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,Eina_Bool enabled EINA_UNUSED)17224 _efl_canvas_textblock_efl_text_format_multiline_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, Eina_Bool enabled EINA_UNUSED)
17225 {
17226    ASYNC_BLOCK;
17227 
17228    if (o->multiline == enabled) return;
17229    o->multiline = enabled;
17230 
17231    if (!o->multiline)
17232      _merge_to_first_text_nodes(obj);
17233    else
17234      _split_text_nodes(obj);
17235 
17236    _canvas_text_format_changed(obj, o);
17237 }
17238 
17239 static Eina_Bool
_efl_canvas_textblock_efl_text_format_multiline_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)17240 _efl_canvas_textblock_efl_text_format_multiline_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
17241 {
17242    return o->multiline;
17243 }
17244 
17245 static void
_efl_canvas_textblock_efl_text_format_text_horizontal_align_auto_type_set(Eo * obj,Efl_Canvas_Textblock_Data * o,Efl_Text_Format_Horizontal_Alignment_Auto_Type type)17246 _efl_canvas_textblock_efl_text_format_text_horizontal_align_auto_type_set(Eo *obj, Efl_Canvas_Textblock_Data *o, Efl_Text_Format_Horizontal_Alignment_Auto_Type type)
17247 {
17248    ASYNC_BLOCK;
17249    if (type == EFL_TEXT_FORMAT_HORIZONTAL_ALIGNMENT_AUTO_TYPE_NONE)
17250      {
17251         _FMT_SET(halign_auto, EVAS_TEXTBLOCK_ALIGN_AUTO_NONE);
17252      }
17253    else if (type == EFL_TEXT_FORMAT_HORIZONTAL_ALIGNMENT_AUTO_TYPE_AUTO)
17254      {
17255         _FMT_SET(halign_auto, EVAS_TEXTBLOCK_ALIGN_AUTO_NORMAL);
17256      }
17257    else if (type == EFL_TEXT_FORMAT_HORIZONTAL_ALIGNMENT_AUTO_TYPE_LOCALE)
17258      {
17259         _FMT_SET(halign_auto, EVAS_TEXTBLOCK_ALIGN_AUTO_LOCALE);
17260      }
17261    else if (type == EFL_TEXT_FORMAT_HORIZONTAL_ALIGNMENT_AUTO_TYPE_OPPOSITE)
17262      {
17263         _FMT_SET(halign_auto, EVAS_TEXTBLOCK_ALIGN_AUTO_END);
17264      }
17265 }
17266 
17267 static Efl_Text_Format_Horizontal_Alignment_Auto_Type
_efl_canvas_textblock_efl_text_format_text_horizontal_align_auto_type_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o)17268 _efl_canvas_textblock_efl_text_format_text_horizontal_align_auto_type_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o)
17269 {
17270    Efl_Text_Format_Horizontal_Alignment_Auto_Type ret =
17271       EFL_TEXT_FORMAT_HORIZONTAL_ALIGNMENT_AUTO_TYPE_NONE;
17272 
17273    if (_FMT(halign_auto) == EVAS_TEXTBLOCK_ALIGN_AUTO_NORMAL)
17274      {
17275         ret = EFL_TEXT_FORMAT_HORIZONTAL_ALIGNMENT_AUTO_TYPE_AUTO;
17276      }
17277    else if (_FMT(halign_auto) == EVAS_TEXTBLOCK_ALIGN_AUTO_END)
17278      {
17279         ret = EFL_TEXT_FORMAT_HORIZONTAL_ALIGNMENT_AUTO_TYPE_OPPOSITE;
17280      }
17281    else if (_FMT(halign_auto) == EVAS_TEXTBLOCK_ALIGN_AUTO_LOCALE)
17282      {
17283         ret = EFL_TEXT_FORMAT_HORIZONTAL_ALIGNMENT_AUTO_TYPE_LOCALE;
17284      }
17285    return ret;
17286 }
17287 
17288 static void
_efl_canvas_textblock_efl_text_format_text_horizontal_align_set(Eo * obj,Efl_Canvas_Textblock_Data * o,double value)17289 _efl_canvas_textblock_efl_text_format_text_horizontal_align_set(Eo *obj, Efl_Canvas_Textblock_Data *o,
17290       double value)
17291 {
17292    ASYNC_BLOCK;
17293    if (EINA_DBL_EQ(_FMT(halign), value)) return;
17294    _FMT(halign_auto) = EVAS_TEXTBLOCK_ALIGN_AUTO_NONE;
17295    _FMT_SETD(halign, value);
17296 }
17297 
17298 static double
_efl_canvas_textblock_efl_text_format_text_horizontal_align_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)17299 _efl_canvas_textblock_efl_text_format_text_horizontal_align_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
17300 {
17301    return _FMT(halign);
17302 }
17303 
17304 static void
_efl_canvas_textblock_efl_text_format_text_vertical_align_set(Eo * obj,Efl_Canvas_Textblock_Data * o,double value)17305 _efl_canvas_textblock_efl_text_format_text_vertical_align_set(Eo *obj, Efl_Canvas_Textblock_Data *o,
17306       double value)
17307 {
17308    ASYNC_BLOCK;
17309    if (!EINA_DBL_EQ(o->valign, value))
17310      {
17311         o->valign = value;
17312         _canvas_text_format_changed(obj, o);
17313      }
17314 }
17315 
17316 static double
_efl_canvas_textblock_efl_text_format_text_vertical_align_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)17317 _efl_canvas_textblock_efl_text_format_text_vertical_align_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
17318 {
17319    return o->valign;
17320 }
17321 
17322 static void
_efl_canvas_textblock_efl_text_format_line_gap_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,double value EINA_UNUSED)17323 _efl_canvas_textblock_efl_text_format_line_gap_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, double value EINA_UNUSED)
17324 {
17325    ASYNC_BLOCK;
17326    double linerelgap = _FMT(linerelgap);
17327    _FMT(linerelgap) = 0.0;
17328 
17329    if (EINA_DBL_EQ(linerelgap, 0.0))
17330      {
17331         _FMT_SETD(linegap, value);
17332      }
17333    else
17334     {
17335         _FMT(linegap) = value;
17336         _FMT(linerelgap) = 0.0;
17337         _canvas_text_format_changed(obj, o);
17338     }
17339 }
17340 
17341 static double
_efl_canvas_textblock_efl_text_format_line_gap_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)17342 _efl_canvas_textblock_efl_text_format_line_gap_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
17343 {
17344    return _FMT(linegap);
17345 }
17346 
17347 static void
_efl_canvas_textblock_efl_text_format_line_rel_gap_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,double value EINA_UNUSED)17348 _efl_canvas_textblock_efl_text_format_line_rel_gap_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, double value EINA_UNUSED)
17349 {
17350    ASYNC_BLOCK;
17351    double linegap = _FMT(linegap);
17352    _FMT(linegap) = 0.0;
17353 
17354    if (EINA_DBL_EQ(linegap, 0.0))
17355      {
17356         _FMT_SETD(linerelgap, value);
17357      }
17358    else
17359     {
17360         _FMT(linerelgap) = value;
17361         _canvas_text_format_changed(obj, o);
17362     }
17363 }
17364 
17365 static double
_efl_canvas_textblock_efl_text_format_line_rel_gap_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)17366 _efl_canvas_textblock_efl_text_format_line_rel_gap_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
17367 {
17368    return _FMT(linerelgap);
17369 }
17370 
17371 static void
_efl_canvas_textblock_efl_text_format_tab_stops_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,int value EINA_UNUSED)17372 _efl_canvas_textblock_efl_text_format_tab_stops_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, int value EINA_UNUSED)
17373 {
17374    ASYNC_BLOCK;
17375    _FMT_SET(tabstops, value);
17376 }
17377 
17378 static int
_efl_canvas_textblock_efl_text_format_tab_stops_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)17379 _efl_canvas_textblock_efl_text_format_tab_stops_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
17380 {
17381    return _FMT(tabstops);
17382 }
17383 
17384 static void
_efl_canvas_textblock_efl_text_format_password_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,Eina_Bool enabled EINA_UNUSED)17385 _efl_canvas_textblock_efl_text_format_password_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, Eina_Bool enabled EINA_UNUSED)
17386 {
17387    ASYNC_BLOCK;
17388    _FMT_SET(password, enabled);
17389 }
17390 
17391 static Eina_Bool
_efl_canvas_textblock_efl_text_format_password_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)17392 _efl_canvas_textblock_efl_text_format_password_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
17393 {
17394    return _FMT(password);
17395 }
17396 
17397 static void
_efl_canvas_textblock_efl_text_format_replacement_char_set(Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED,const char * repch EINA_UNUSED)17398 _efl_canvas_textblock_efl_text_format_replacement_char_set(Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED, const char *repch EINA_UNUSED)
17399 {
17400    ASYNC_BLOCK;
17401    Eina_Stringshare *nrepch;
17402    if (o->repch != repch)
17403      {
17404         nrepch = eina_stringshare_add(repch);
17405         if (nrepch == _FMT_INFO(font_fallbacks))
17406           {
17407              /* Already stringshared here, unref */
17408              eina_stringshare_del(nrepch);
17409           }
17410         else
17411           {
17412              // Set immediately, load repch later
17413              o->repch = nrepch;
17414              _canvas_text_format_changed(obj, o);
17415           }
17416      }
17417 }
17418 
17419 static const char *
_efl_canvas_textblock_efl_text_format_replacement_char_get(const Eo * obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o EINA_UNUSED)17420 _efl_canvas_textblock_efl_text_format_replacement_char_get(const Eo *obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o EINA_UNUSED)
17421 {
17422    return o->repch;
17423 }
17424 
17425 /**
17426  * @}
17427  */
17428 
17429 #ifdef HAVE_TESTS
17430 /* return EINA_FALSE on error, used in unit_testing */
17431 EAPI Eina_Bool
_evas_textblock_check_item_node_link(Evas_Object * eo_obj)17432 _evas_textblock_check_item_node_link(Evas_Object *eo_obj)
17433 {
17434    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
17435    Evas_Object_Textblock_Paragraph *par;
17436    Evas_Object_Textblock_Line *ln;
17437    Evas_Object_Textblock_Item *it;
17438 
17439    if (!o) return EINA_FALSE;
17440 
17441    _relayout_if_needed(eo_obj, o);
17442 
17443    EINA_INLIST_FOREACH(o->paragraphs, par)
17444      {
17445         EINA_INLIST_FOREACH(par->lines, ln)
17446           {
17447              EINA_INLIST_FOREACH(ln->items, it)
17448                {
17449                   if (it->text_node != par->text_node)
17450                      return EINA_FALSE;
17451                }
17452           }
17453      }
17454    return EINA_TRUE;
17455 }
17456 
17457 EAPI int
_evas_textblock_format_offset_get(const Evas_Object_Textblock_Node_Format * n)17458 _evas_textblock_format_offset_get(const Evas_Object_Textblock_Node_Format *n)
17459 {
17460    return n->offset;
17461 }
17462 #endif
17463 
17464 #if 0
17465 /* Good for debugging */
17466 EAPI void
17467 pfnode(Evas_Object_Textblock_Node_Format *n)
17468 {
17469    printf("Format Node: %p\n", n);
17470    printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
17471    printf("text_node = %p, offset = %u, visible = %d\n", n->text_node, (unsigned int) n->offset, n->visible);
17472    printf("'%s'\n", n->format);
17473 }
17474 
17475 EAPI void
17476 ptnode(Evas_Object_Textblock_Node_Text *n)
17477 {
17478    printf("Text Node: %p\n", n);
17479    printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
17480    printf("format_node = %p\n", n->format_node);
17481    printf("'%ls'\n", eina_ustrbuf_string_get(n->unicode));
17482 }
17483 
17484 EAPI void
17485 pitem(Evas_Object_Textblock_Item *it)
17486 {
17487    Evas_Object_Textblock_Text_Item *ti;
17488    Evas_Object_Textblock_Format_Item *fi;
17489    printf("Item: %p %s\n", it, (it->visually_deleted) ? "(visually deleted)" : "");
17490    printf("Type: %s (%d)\n", (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
17491          "TEXT" : "FORMAT", it->type);
17492    printf("Text pos: %u Visual pos: %u\n", (unsigned int) it->text_pos, (unsigned int)
17493 #ifdef BIDI_SUPPORT
17494          it->visual_pos
17495 #else
17496          it->text_pos
17497 #endif
17498          );
17499    printf("Coords: x = %d w = %d adv = %d\n", (int) it->x, (int) it->w,
17500          (int) it->adv);
17501    if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
17502      {
17503         Eina_Unicode *tmp;
17504         ti = _ITEM_TEXT(it);
17505         tmp = eina_unicode_strdup(GET_ITEM_TEXT(ti));
17506         tmp[ti->text_props.text_len] = '\0';
17507         printf("Text: '%ls'\n", tmp);
17508         free(tmp);
17509      }
17510    else
17511      {
17512         fi = _ITEM_FORMAT(it);
17513         printf("Format: '%s'\n", fi->item);
17514      }
17515 }
17516 
17517 EAPI void
17518 ppar(Evas_Object_Textblock_Paragraph *par)
17519 {
17520    Evas_Object_Textblock_Item *it;
17521    Eina_List *i;
17522    EINA_LIST_FOREACH(par->logical_items, i, it)
17523      {
17524         printf("***********************\n");
17525         pitem(it);
17526      }
17527 }
17528 
17529 #endif
17530 
17531 #define EFL_CANVAS_TEXTBLOCK_EXTRA_OPS \
17532    EFL_OBJECT_OP_FUNC(efl_dbg_info_get, _efl_canvas_textblock_efl_object_dbg_info_get)
17533 
17534 EAPI Efl_Text_Cursor_Handle *
evas_object_textblock_cursor_get(const Evas_Object * eo_obj EINA_UNUSED)17535 evas_object_textblock_cursor_get(const Evas_Object *eo_obj EINA_UNUSED)
17536 {
17537    TB_HEAD_RETURN(NULL);
17538    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
17539    return o->cursor;
17540 }
17541 
17542 /* Async Layout */
17543 
17544 typedef struct _Text_Promise_Ctx Text_Promise_Ctx;
17545 struct _Text_Promise_Ctx
17546 {
17547    int ret;
17548    Ctxt *c;
17549    Evas_Coord style_pad_l, style_pad_r, style_pad_t, style_pad_b;
17550    Evas_Coord *w_ret, *h_ret;
17551    Eina_Promise *p;
17552    Eina_Value *v;
17553 };
17554 
17555 static void
_text_layout_async_do(void * todo,Ecore_Thread * thread EINA_UNUSED)17556 _text_layout_async_do(void *todo, Ecore_Thread *thread EINA_UNUSED)
17557 {
17558    Text_Promise_Ctx *td = todo;
17559    _layout_visual(td->c);
17560 }
17561 
17562 static void
_resolve_async(Text_Promise_Ctx * td,Evas_Coord w,Evas_Coord h)17563 _resolve_async(Text_Promise_Ctx *td, Evas_Coord w, Evas_Coord h)
17564 {
17565    Eina_Value v;
17566    Eina_Rectangle r = { 0, 0, w, h };
17567    eina_value_setup(&v, EINA_VALUE_TYPE_RECTANGLE);
17568    eina_value_set(&v, r);
17569    eina_promise_resolve(td->p, v);
17570    free(td);
17571 }
17572 
17573 static void
_text_layout_async_done(void * todo,Ecore_Thread * thread EINA_UNUSED)17574 _text_layout_async_done(void *todo, Ecore_Thread *thread EINA_UNUSED)
17575 {
17576    Text_Promise_Ctx *td = todo;
17577    Ctxt *c = td->c;
17578    Eo *obj = c->obj;
17579    Efl_Canvas_Textblock_Data *o = c->o;
17580    Evas_Coord w_ret, h_ret;
17581    _layout_done(c, &w_ret, &h_ret);
17582 
17583    c->o->formatted.valid = 1;
17584    c->o->formatted.oneline_h = 0;
17585    c->o->last_w = c->evas_o->cur->geometry.w;
17586    c->o->wrap_changed = EINA_FALSE;
17587    c->o->last_h = c->evas_o->cur->geometry.h;
17588    if ((c->o->paragraphs) && (!EINA_INLIST_GET(c->o->paragraphs)->next) &&
17589        (c->o->paragraphs->lines) && (!EINA_INLIST_GET(c->o->paragraphs->lines)->next))
17590      {
17591         if (c->evas_o->cur->geometry.h < c->o->formatted.h + c->o->style_pad.t + c->o->style_pad.b)
17592           {
17593              c->o->formatted.oneline_h = c->o->formatted.h + c->o->style_pad.t + c->o->style_pad.b;
17594           }
17595      }
17596    c->o->changed = 0;
17597    c->o->content_changed = 0;
17598    c->o->format_changed = EINA_FALSE;
17599    c->o->redraw = 1;
17600 #ifdef BIDI_SUPPORT
17601    c->o->changed_paragraph_direction = EINA_FALSE;
17602 #endif
17603 
17604    o->formatted.w = c->wmax;
17605    o->formatted.h = c->hmax;
17606    c->o->changed = EINA_TRUE;
17607    evas_object_change(c->obj, c->evas_o);
17608    free(c);
17609 
17610    _resolve_async(td,
17611                   o->formatted.w + o->style_pad.l + o->style_pad.r,
17612                   o->formatted.h + o->style_pad.t + o->style_pad.b);
17613 
17614    o->layout_th = NULL;
17615    o->layout_jobs--;
17616    if (o->layout_jobs > 0)
17617      {
17618         efl_canvas_textblock_async_layout(obj);
17619      }
17620 }
17621 
17622 static void
_dummy_cancel(void * data EINA_UNUSED,const Eina_Promise * dead EINA_UNUSED)17623 _dummy_cancel(void *data EINA_UNUSED, const Eina_Promise *dead EINA_UNUSED)
17624 {
17625 }
17626 
17627 static Eina_Future_Scheduler *
_future_scheduler_get(void)17628 _future_scheduler_get(void)
17629 {
17630    return efl_loop_future_scheduler_get(efl_main_loop_get());
17631 }
17632 
17633 EOLIAN static Eina_Future *
_efl_canvas_textblock_async_layout(Eo * eo_obj EINA_UNUSED,Efl_Canvas_Textblock_Data * o)17634 _efl_canvas_textblock_async_layout(Eo *eo_obj EINA_UNUSED, Efl_Canvas_Textblock_Data *o)
17635 {
17636    Ctxt *c;
17637    Eina_Promise *p;
17638    Eina_Future *f;
17639    Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
17640 
17641    Text_Promise_Ctx *ctx = calloc(1, sizeof(*ctx));
17642    p = eina_promise_new(_future_scheduler_get(), _dummy_cancel, ctx);
17643    if (!p)
17644      {
17645         CRI("Failed to allocate a promise");
17646         return NULL;
17647      }
17648    ctx->p = p;
17649    f = eina_future_new(p);
17650 
17651    if (o->layout_th)
17652      {
17653         o->layout_jobs++;
17654         return f;
17655      }
17656    evas_object_textblock_coords_recalc(eo_obj, obj, obj->private_data);
17657    if (o->formatted.valid)
17658      {
17659         _resolve_async(ctx,
17660                        o->formatted.w + o->style_pad.l + o->style_pad.r,
17661                        o->formatted.h + o->style_pad.t + o->style_pad.b);
17662         return f;
17663      }
17664 
17665    c = calloc(1, sizeof(*c));
17666    ctx->c = c;
17667 
17668    if (!_layout_setup(c, eo_obj,
17669             obj->cur->geometry.w, obj->cur->geometry.h))
17670      {
17671         _resolve_async(ctx, 0, 0);
17672         return f;
17673      }
17674    _layout_pre(c);
17675    o->layout_th = ecore_thread_run(_text_layout_async_do, _text_layout_async_done,
17676          NULL, ctx);
17677    return f;
17678 }
17679 /* Fitting Internal Functions*/
17680 
fit_cache_clear(TEXT_FIT_CONTENT_CONFIG * fc,unsigned int fit_cache_flags)17681 int fit_cache_clear(TEXT_FIT_CONTENT_CONFIG *fc, unsigned int fit_cache_flags)
17682 {
17683    EINA_SAFETY_ON_NULL_RETURN_VAL(fc, EVAS_ERROR_INVALID_PARAM);
17684    if ((fit_cache_flags&FIT_CACHE_CANVAS_SIZE) == FIT_CACHE_CANVAS_SIZE)
17685      fc->last_size = EINA_SIZE2D(0, 0);
17686    if ((fit_cache_flags&FIT_CACHE_INTERNAL_SIZE_ARRAY) == FIT_CACHE_INTERNAL_SIZE_ARRAY)
17687      for(int i = 0 ; i < 255 ; i++) fc->size_cache[i] = EINA_SIZE2D(0,0);
17688    if ((fit_cache_flags&FIT_CACHE_FORCE_REFIT) == FIT_CACHE_FORCE_REFIT)
17689      fc->force_refit = EINA_TRUE;
17690    return EVAS_ERROR_SUCCESS;
17691 }
17692 
fit_start_fitting(Evas_Object * eo_obj)17693 int fit_start_fitting(Evas_Object *eo_obj)
17694 {
17695    EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, EVAS_ERROR_INVALID_PARAM);
17696    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
17697    if (o->fit_in_progress == EINA_TRUE)
17698      return EVAS_ERROR_INVALID_OPERATION;
17699 
17700    o->fit_in_progress = EINA_TRUE;
17701    return EVAS_ERROR_SUCCESS;
17702 }
17703 
fit_finish_fitting(Evas_Object * eo_obj)17704 int fit_finish_fitting(Evas_Object *eo_obj)
17705 {
17706    EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, EVAS_ERROR_INVALID_PARAM);
17707    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
17708    if (o->fit_in_progress == EINA_FALSE)
17709      return EVAS_ERROR_INVALID_OPERATION;
17710 
17711    o->fit_in_progress = EINA_FALSE;
17712    return EVAS_ERROR_SUCCESS;
17713 }
17714 
fit_is_fitting(const Evas_Object * eo_obj)17715 Eina_Bool fit_is_fitting(const Evas_Object *eo_obj)
17716 {
17717    EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, EVAS_ERROR_INVALID_PARAM);
17718    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
17719    return o->fit_in_progress;
17720 }
17721 
fit_text_block(Evas_Object * eo_obj)17722 int fit_text_block(Evas_Object *eo_obj)
17723 {
17724    EINA_SAFETY_ON_NULL_RETURN_VAL(eo_obj, EVAS_ERROR_INVALID_PARAM);
17725    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
17726    Evas_Coord x,y,w,h;
17727    Evas_Coord wf_new,hf_new;
17728 
17729    TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config;
17730 
17731    if (fc->options == TEXTBLOCK_FIT_MODE_NONE && !fc->force_refit)
17732      return EVAS_ERROR_SUCCESS;
17733 
17734 
17735    if (fc->options == TEXTBLOCK_FIT_MODE_NONE)
17736      {
17737         fit_start_fitting(eo_obj);
17738         fc->force_refit = 0;
17739         fit_style_update(eo_obj,-1, EINA_FALSE, EINA_FALSE);
17740         fit_finish_fitting(eo_obj);
17741         return EVAS_ERROR_SUCCESS;
17742      }
17743 
17744    evas_object_geometry_get(eo_obj, &x, &y, &w, &h);
17745 
17746    if (w > 0 && h > 0)
17747      {
17748         Eina_Bool b_fit_width = ((fc->options & TEXTBLOCK_FIT_MODE_WIDTH) == TEXTBLOCK_FIT_MODE_WIDTH);
17749         Eina_Bool b_fit_height = ((fc->options & TEXTBLOCK_FIT_MODE_HEIGHT) == TEXTBLOCK_FIT_MODE_HEIGHT);
17750         //FIXME uncomment condition when style is not warp
17751         if ( fc->force_refit || /*(w != fc->last_size.w && b_fit_width) || (h != fc->last_size.h && b_fit_height)*/ EINA_TRUE)
17752           {
17753              /* Extra Check to reduce recalculate */
17754              Eina_Bool b_max_reached = (fc->last_size_index == ((int)fc->size_list_length) - 1);
17755              Eina_Bool b_min_reached = (fc->last_size_index == 0);
17756              /* 1 - If max font size reached and text block size increased*/
17757              if (!fc->force_refit && b_max_reached && ((b_fit_width ? (w >= fc->last_size.w) : EINA_TRUE) && (b_fit_height ? (h >= fc->last_size.h) : EINA_TRUE)))
17758                return EVAS_ERROR_SUCCESS;
17759              /* 2- If min font size reached and text block size decreased*/
17760              if (!fc->force_refit && b_min_reached && ((b_fit_width ? (w <= fc->last_size.w) : EINA_TRUE) && (b_fit_height ? (h <= fc->last_size.h) : EINA_TRUE)))
17761                {
17762                   /*This is needed to recalculate ellipsis, inside fitting to avoid losing markup_text*/
17763                   fit_start_fitting(eo_obj);
17764                   _canvas_text_format_changed(eo_obj, o);
17765                   fit_finish_fitting(eo_obj);
17766                   return EVAS_ERROR_SUCCESS;
17767                }
17768 
17769              fit_start_fitting(eo_obj);
17770 
17771              fc->force_refit = EINA_FALSE;
17772              fc->last_size.w = w;
17773              fc->last_size.h = h;
17774 
17775              int r = fc->size_list_length;
17776              int l = 0;
17777 
17778              Eina_Bool bwrap = EINA_FALSE;
17779              if (fc->options == TEXTBLOCK_FIT_MODE_WIDTH)
17780                {
17781                   bwrap = EINA_TRUE;
17782                }
17783 
17784              while(r > l)
17785                {
17786                   int mid = (r + l) / 2;
17787                   /*cache font sizes vaules from 0-255 in size_cache array*/
17788                   size_t font_size = fc->p_size_array[mid];
17789                   if (font_size <= 0xFF && (fc->size_cache[font_size].w != 0 && fc->size_cache[font_size].h != 0))
17790                     {
17791                         wf_new = fc->size_cache[font_size].w;
17792                         hf_new = fc->size_cache[font_size].h;
17793                     }
17794                   else
17795                     {
17796                        fit_style_update(eo_obj,fc->p_size_array[mid],EINA_TRUE,bwrap);
17797                        Eina_Size2D size = efl_canvas_textblock_size_formatted_get(eo_obj);
17798                        wf_new = size.w;
17799                        hf_new = size.h;
17800                        if (fc->p_size_array[mid]<255)
17801                          {
17802                              fc->size_cache[font_size].w = wf_new;
17803                              fc->size_cache[font_size].h = hf_new;
17804                          }
17805                     }
17806 
17807                   if (
17808                       ((wf_new > w) & ((fc->options & TEXTBLOCK_FIT_MODE_WIDTH) == TEXTBLOCK_FIT_MODE_WIDTH)) ||
17809                       ((hf_new > h) & ((fc->options & TEXTBLOCK_FIT_MODE_HEIGHT) == TEXTBLOCK_FIT_MODE_HEIGHT)))
17810                     {
17811                        r = mid;
17812                     }
17813                   else
17814                     {
17815                        l = mid + 1;
17816                     }
17817                 }
17818 
17819                 /*Lower bound founded, subtract one to move for nearest value*/
17820                 fc->last_size_index = MAX(l-1, 0);
17821                 fit_style_update(eo_obj,fc->p_size_array[fc->last_size_index],(fc->last_size_index != 0) && fc->options != TEXTBLOCK_FIT_MODE_HEIGHT ,EINA_FALSE);
17822                 fit_finish_fitting(eo_obj);
17823           }
17824      }
17825    return EVAS_ERROR_SUCCESS;
17826 }
17827 
fit_fill_internal_list(TEXT_FIT_CONTENT_CONFIG * fc)17828 int fit_fill_internal_list(TEXT_FIT_CONTENT_CONFIG *fc)
17829 {
17830    int diff = (fc->max_font_size - fc->min_font_size);
17831    if (fc->p_size_array)
17832      {
17833         free(fc->p_size_array);
17834         fc->p_size_array = NULL;
17835      }
17836    if (diff == 0)
17837      {
17838         fc->size_list_length = 1;
17839         fc->p_size_array = malloc(sizeof(unsigned int) * fc->size_list_length);
17840         if (!fc->p_size_array)
17841            return EVAS_ERROR_NO_MEMORY;
17842         fc->p_size_array[0] = fc->max_font_size;
17843         return EVAS_ERROR_SUCCESS;
17844      }
17845 
17846    fc->size_list_length = 2 + diff / MAX(fc->step_size, 1);
17847    fc->p_size_array = malloc(sizeof(unsigned int) * fc->size_list_length);
17848    if (!fc->p_size_array)
17849      return EVAS_ERROR_NO_MEMORY;
17850 
17851    size_t i ;
17852    for (i = 0 ; i < fc->size_list_length - 1; i++)
17853      {
17854         fc->p_size_array[i] = fc->min_font_size + i * MAX(fc->step_size, 1);
17855      }
17856    fc->p_size_array[fc->size_list_length - 1] = fc->max_font_size;
17857    fc->last_size_index = -1;
17858    return EVAS_ERROR_SUCCESS;
17859 }
17860 
17861 
17862 
evas_textblock_fit_options_set(Evas_Object * obj,unsigned int options)17863 EAPI int evas_textblock_fit_options_set(Evas_Object *obj,  unsigned int options)
17864 {
17865    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM);
17866    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS);
17867    TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config;
17868    if (fc->options == options)
17869      return EVAS_ERROR_SUCCESS;
17870 
17871    fc->options = options;
17872    fit_cache_clear(fc, FIT_CACHE_ALL);
17873    fit_text_block(obj);
17874    return EVAS_ERROR_SUCCESS;
17875 }
17876 
evas_textblock_fit_options_get(const Evas_Object * obj,unsigned int * p_options)17877 EAPI int evas_textblock_fit_options_get(const Evas_Object *obj,  unsigned int *p_options)
17878 {
17879    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM);
17880    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS);
17881    TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config;
17882    if (p_options)
17883      *p_options = fc->options;
17884    return EVAS_ERROR_SUCCESS;
17885 }
17886 
evas_textblock_fit_size_range_set(Evas_Object * obj,unsigned int min_font_size,unsigned int max_font_size)17887 EAPI int evas_textblock_fit_size_range_set(Evas_Object *obj,  unsigned int min_font_size, unsigned int max_font_size)
17888 {
17889    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM);
17890    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS);
17891    TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config;
17892 
17893    Eina_Bool max_changed = fc->max_font_size != max_font_size;
17894    Eina_Bool min_changed = fc->min_font_size != min_font_size;
17895 
17896    /* last_selected_size used for optimization calculations
17897     * If last_size_index already recach last element in p_size_array
17898     * Skip optimization by setting last_selected_size to -1
17899    */
17900    int last_selected_size = fc->last_size_index;
17901    if (last_selected_size == ((int)fc->size_list_length-1))
17902      last_selected_size = -1;
17903 
17904    if (!max_changed && !min_changed)
17905      return EVAS_ERROR_SUCCESS;
17906 
17907    if (max_font_size < min_font_size)
17908      return EVAS_ERROR_INVALID_PARAM;
17909 
17910    fc->max_font_size = max_font_size;
17911    fc->min_font_size = min_font_size;
17912 
17913    int n_ret = EVAS_ERROR_SUCCESS;
17914    n_ret = fit_cache_clear(fc,FIT_CACHE_FORCE_REFIT);
17915    if (n_ret) return n_ret;
17916    n_ret = fit_fill_internal_list(fc);
17917    if (n_ret) return n_ret;
17918 
17919    /* Optimization to reduce calculations
17920     * If only max size changed and last fit size index is still valid, then no need to recalculation
17921     * Where changing max font size will not change content of p_size_array for sizes < max_size
17922    */
17923    if (min_changed || (last_selected_size == -1 || last_selected_size > ((int)fc->size_list_length-1)))
17924      {
17925         n_ret = fit_text_block(obj);
17926         if (n_ret) return n_ret;
17927      }
17928    else
17929      {
17930         /* Keep fit size index */
17931         fc->last_size_index = last_selected_size;
17932      }
17933    return EVAS_ERROR_SUCCESS;
17934 }
17935 
evas_textblock_fit_size_range_get(const Evas_Object * obj,unsigned int * p_min_font_size,unsigned int * p_max_font_size)17936 EAPI int evas_textblock_fit_size_range_get(const Evas_Object *obj,  unsigned int *p_min_font_size, unsigned int *p_max_font_size)
17937 {
17938    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM);
17939    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS);
17940    TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config;
17941 
17942    if (p_min_font_size)
17943      *p_min_font_size = fc->min_font_size;
17944 
17945    if (p_max_font_size)
17946      *p_max_font_size = fc->max_font_size;
17947 
17948    return EVAS_ERROR_SUCCESS;
17949 }
17950 
evas_textblock_fit_step_size_set(Evas_Object * obj,unsigned int step_size)17951 EAPI int evas_textblock_fit_step_size_set(Evas_Object *obj,  unsigned int step_size)
17952 {
17953    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM);
17954    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS);
17955    TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config;
17956    if (fc->step_size == step_size)
17957      return EVAS_ERROR_SUCCESS;
17958 
17959    if (step_size == 0)
17960      return EVAS_ERROR_INVALID_PARAM;
17961 
17962    fc->step_size = step_size;
17963    int n_ret = EVAS_ERROR_SUCCESS;
17964    n_ret = fit_cache_clear(fc, FIT_CACHE_FORCE_REFIT);
17965    if (n_ret) return n_ret;
17966    n_ret = fit_fill_internal_list(fc);
17967    if (n_ret) return n_ret;
17968    n_ret = fit_text_block(obj);
17969    if (n_ret) return n_ret;
17970    return EVAS_ERROR_SUCCESS;
17971 }
17972 
evas_textblock_fit_step_size_get(const Evas_Object * obj,unsigned int * p_step_size)17973 EAPI int evas_textblock_fit_step_size_get(const Evas_Object *obj,  unsigned int * p_step_size)
17974 {
17975    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM);
17976    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS);
17977    TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config;
17978    if (p_step_size)
17979      *p_step_size = fc->step_size;
17980    return EVAS_ERROR_SUCCESS;
17981 }
17982 
compareUINT(const void * a,const void * b)17983 int compareUINT(const void * a, const void * b)
17984 {
17985    unsigned int a_value = *(const unsigned int*)a;
17986    unsigned int b_value = *(const unsigned int*)b;
17987 
17988    if(a_value > b_value) return 1;
17989    else if(a_value < b_value) return -1;
17990    else return 0;
17991 }
17992 
evas_textblock_fit_size_array_set(Evas_Object * obj,const unsigned int * p_size_array,size_t size_array_len)17993 EAPI int evas_textblock_fit_size_array_set(Evas_Object *obj, const unsigned int *p_size_array, size_t size_array_len)
17994 {
17995    int n_ret = EVAS_ERROR_SUCCESS;
17996    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM);
17997    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS);
17998    TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config;
17999    if (size_array_len == 0)
18000      return EVAS_ERROR_INVALID_PARAM;
18001 
18002    if (fc->p_size_array)
18003      {
18004         free(fc->p_size_array);
18005         fc->p_size_array = NULL;
18006         fc->size_list_length = 0;
18007      }
18008 
18009    fc->p_size_array = malloc(sizeof(unsigned int) * size_array_len);
18010    if (!fc->p_size_array) return EVAS_ERROR_NO_MEMORY;
18011    memcpy(fc->p_size_array,p_size_array,sizeof(unsigned int) * size_array_len);
18012    fc->size_list_length = size_array_len;
18013 
18014    fc->last_size_index = -1;
18015 
18016    qsort(fc->p_size_array,fc->size_list_length,sizeof(unsigned int),compareUINT);
18017 
18018    n_ret = fit_cache_clear(fc, FIT_CACHE_FORCE_REFIT);
18019    if (n_ret) return n_ret;
18020    n_ret = fit_text_block(obj);
18021    if (n_ret) return n_ret;
18022    return EVAS_ERROR_SUCCESS;
18023 }
18024 
evas_textblock_fit_size_array_get(const Evas_Object * obj,unsigned int * p_size_array,size_t * p_size_array_len,size_t passed_array_size)18025 EAPI int evas_textblock_fit_size_array_get(const Evas_Object *obj, unsigned int *p_size_array, size_t *p_size_array_len, size_t passed_array_size)
18026 {
18027    EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EVAS_ERROR_INVALID_PARAM);
18028    Efl_Canvas_Textblock_Data *o = efl_data_scope_get(obj, MY_CLASS);
18029    TEXT_FIT_CONTENT_CONFIG * fc = &o->fit_content_config;
18030    if (p_size_array)
18031      {
18032         size_t num = MIN(passed_array_size,fc->size_list_length);
18033         memcpy(p_size_array,fc->p_size_array,sizeof(unsigned int)* num);
18034      }
18035    if (p_size_array_len)
18036      {
18037         *p_size_array_len = fc->size_list_length;
18038      }
18039    return EVAS_ERROR_SUCCESS;
18040 }
18041 
18042 #include "canvas/efl_canvas_textblock.eo.c"
18043 #include "canvas/efl_canvas_textblock_eo.legacy.c"
18044 #include "canvas/efl_canvas_textblock_factory.eo.c" // interface
18045