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("&", "\x26"),
987 ESCAPE_VALUE("'", "\x27"),
988 ESCAPE_VALUE(">", "\x3e"),
989 ESCAPE_VALUE("<", "\x3c"),
990 ESCAPE_VALUE(""", "\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("Á", "\xc3\x81"),
1002 ESCAPE_VALUE("Â", "\xc3\x82"),
1003 ESCAPE_VALUE("&Aelig;", "\xc3\x86"),
1004 ESCAPE_VALUE("À", "\xc3\x80"),
1005 ESCAPE_VALUE("Å", "\xc3\x85"),
1006 ESCAPE_VALUE("Ã", "\xc3\x83"),
1007 ESCAPE_VALUE("Ä", "\xc3\x84"),
1008 ESCAPE_VALUE("Ç", "\xc3\x87"),
1009 ESCAPE_VALUE("‡", "\xe2\x80\xa1"),
1010 ESCAPE_VALUE("É", "\xc3\x89"),
1011 ESCAPE_VALUE("Ê", "\xc3\x8a"),
1012 ESCAPE_VALUE("È", "\xc3\x88"),
1013 ESCAPE_VALUE("&Eth;", "\xc3\x90"),
1014 ESCAPE_VALUE("Ë", "\xc3\x8b"),
1015 ESCAPE_VALUE("Í", "\xc3\x8d"),
1016 ESCAPE_VALUE("Î", "\xc3\x8e"),
1017 ESCAPE_VALUE("Ì", "\xc3\x8c"),
1018 ESCAPE_VALUE("Ï", "\xc3\x8f"),
1019 ESCAPE_VALUE("Ñ", "\xc3\x91"),
1020 ESCAPE_VALUE("Ó", "\xc3\x93"),
1021 ESCAPE_VALUE("Ô", "\xc3\x94"),
1022 ESCAPE_VALUE("Ò", "\xc3\x92"),
1023 ESCAPE_VALUE("Ø", "\xc3\x98"),
1024 ESCAPE_VALUE("Õ", "\xc3\x95"),
1025 ESCAPE_VALUE("Ö", "\xc3\x96"),
1026 ESCAPE_VALUE("&Thorn;", "\xc3\x9e"),
1027 ESCAPE_VALUE("Ú", "\xc3\x9a"),
1028 ESCAPE_VALUE("Û", "\xc3\x9b"),
1029 ESCAPE_VALUE("Ù", "\xc3\x99"),
1030 ESCAPE_VALUE("Ý", "\xc3\x9d"),
1031 ESCAPE_VALUE("á", "\xc3\xa1"),
1032 ESCAPE_VALUE("â", "\xc3\xa2"),
1033 ESCAPE_VALUE("´", "\xc2\xb4"),
1034 ESCAPE_VALUE("æ", "\xc3\xa6"),
1035 ESCAPE_VALUE("à", "\xc3\xa0"),
1036 ESCAPE_VALUE("α", "\xce\x91"),
1037 ESCAPE_VALUE("∧", "\xe2\x88\xa7"),
1038 ESCAPE_VALUE("å", "\xc3\xa5"),
1039 ESCAPE_VALUE("ã", "\xc3\xa3"),
1040 ESCAPE_VALUE("ä", "\xc3\xa4"),
1041 ESCAPE_VALUE("β", "\xce\x92"),
1042 ESCAPE_VALUE("¦", "\xc2\xa6"),
1043 ESCAPE_VALUE("•", "\xe2\x80\xa2"),
1044 ESCAPE_VALUE("ç", "\xc3\xa7"),
1045 ESCAPE_VALUE("¸", "\xc2\xb8"),
1046 ESCAPE_VALUE("¢", "\xc2\xa2"),
1047 ESCAPE_VALUE("χ", "\xce\xa7"),
1048 ESCAPE_VALUE("©", "\xc2\xa9"),
1049 ESCAPE_VALUE("¤", "\xc2\xa4"),
1050 ESCAPE_VALUE("†", "\xe2\x80\xa0"),
1051 ESCAPE_VALUE("↓", "\xe2\x86\x93"),
1052 ESCAPE_VALUE("°", "\xc2\xb0"),
1053 ESCAPE_VALUE("δ", "\xce\x94"),
1054 ESCAPE_VALUE("÷", "\xc3\xb7"),
1055 ESCAPE_VALUE("é", "\xc3\xa9"),
1056 ESCAPE_VALUE("ê", "\xc3\xaa"),
1057 ESCAPE_VALUE("è", "\xc3\xa8"),
1058 ESCAPE_VALUE("ε", "\xce\x95"),
1059 ESCAPE_VALUE("≡", "\xe2\x89\xa1"),
1060 ESCAPE_VALUE("η", "\xce\x97"),
1061 ESCAPE_VALUE("ð", "\xc3\xb0"),
1062 ESCAPE_VALUE("ë", "\xc3\xab"),
1063 ESCAPE_VALUE("€", "\xe2\x82\xac"),
1064 ESCAPE_VALUE("∃", "\xe2\x88\x83"),
1065 ESCAPE_VALUE("∀", "\xe2\x88\x80"),
1066 ESCAPE_VALUE("½", "\xc2\xbd"),
1067 ESCAPE_VALUE("¼", "\xc2\xbc"),
1068 ESCAPE_VALUE("¾", "\xc2\xbe"),
1069 ESCAPE_VALUE("γ", "\xce\x93"),
1070 ESCAPE_VALUE("↔", "\xe2\x86\x94"),
1071 ESCAPE_VALUE("…", "\xe2\x80\xa6"),
1072 ESCAPE_VALUE("í", "\xc3\xad"),
1073 ESCAPE_VALUE("î", "\xc3\xae"),
1074 ESCAPE_VALUE("¡", "\xc2\xa1"),
1075 ESCAPE_VALUE("ì", "\xc3\xac"),
1076 ESCAPE_VALUE("∫", "\xe2\x88\xab"),
1077 ESCAPE_VALUE("ι", "\xce\x99"),
1078 ESCAPE_VALUE("¿", "\xc2\xbf"),
1079 ESCAPE_VALUE("ï", "\xc3\xaf"),
1080 ESCAPE_VALUE("κ", "\xce\x9a"),
1081 ESCAPE_VALUE("λ", "\xce\x9b"),
1082 ESCAPE_VALUE("«", "\xc2\xab"),
1083 ESCAPE_VALUE("←", "\xe2\x86\x90"),
1084 ESCAPE_VALUE("←", "\xe2\x87\x90"),
1085 ESCAPE_VALUE("‎", "\xe2\x80\x8e"),
1086 ESCAPE_VALUE("¯", "\xc2\xaf"),
1087 ESCAPE_VALUE("µ", "\xc2\xb5"),
1088 ESCAPE_VALUE("·", "\xc2\xb7"),
1089 ESCAPE_VALUE("μ", "\xce\x9c"),
1090 ESCAPE_VALUE("∇", "\xe2\x88\x87"),
1091 ESCAPE_VALUE(" ", "\xc2\xa0"),
1092 ESCAPE_VALUE("≠", "\xe2\x89\xa0"),
1093 ESCAPE_VALUE("¬", "\xc2\xac"),
1094 ESCAPE_VALUE("ñ", "\xc3\xb1"),
1095 ESCAPE_VALUE("ν", "\xce\x9d"),
1096 ESCAPE_VALUE("ó", "\xc3\xb3"),
1097 ESCAPE_VALUE("ô", "\xc3\xb4"),
1098 ESCAPE_VALUE("ò", "\xc3\xb2"),
1099 ESCAPE_VALUE("ω", "\xce\xa9"),
1100 ESCAPE_VALUE("ο", "\xce\x9f"),
1101 ESCAPE_VALUE("⊕", "\xe2\x8a\x95"),
1102 ESCAPE_VALUE("∨", "\xe2\x88\xa8"),
1103 ESCAPE_VALUE("ª", "\xc2\xaa"),
1104 ESCAPE_VALUE("º", "\xc2\xba"),
1105 ESCAPE_VALUE("ø", "\xc3\xb8"),
1106 ESCAPE_VALUE("õ", "\xc3\xb5"),
1107 ESCAPE_VALUE("ö", "\xc3\xb6"),
1108 ESCAPE_VALUE("¶", "\xc2\xb6"),
1109 ESCAPE_VALUE("⊥", "\xe2\x8a\xa5"),
1110 ESCAPE_VALUE("φ", "\xce\xa6"),
1111 ESCAPE_VALUE("π", "\xce\xa0"),
1112 ESCAPE_VALUE("±", "\xc2\xb1"),
1113 ESCAPE_VALUE("£", "\xc2\xa3"),
1114 ESCAPE_VALUE("∏", "\xe2\x88\x8f"),
1115 ESCAPE_VALUE("ψ", "\xce\xa8"),
1116 ESCAPE_VALUE("»", "\xc2\xbb"),
1117 ESCAPE_VALUE("→", "\xe2\x86\x92"),
1118 ESCAPE_VALUE("⇒", "\xe2\x87\x92"),
1119 ESCAPE_VALUE("®", "\xc2\xae"),
1120 ESCAPE_VALUE("ρ", "\xce\xa1"),
1121 ESCAPE_VALUE("‏", "\xe2\x80\x8f"),
1122 ESCAPE_VALUE("§", "\xc2\xa7"),
1123 ESCAPE_VALUE("­", "\xc2\xad"),
1124 ESCAPE_VALUE("σ", "\xce\xa3"),
1125 ESCAPE_VALUE("∑", "\xe2\x88\x91"),
1126 ESCAPE_VALUE("¹", "\xc2\xb9"),
1127 ESCAPE_VALUE("²", "\xc2\xb2"),
1128 ESCAPE_VALUE("³", "\xc2\xb3"),
1129 ESCAPE_VALUE("ß", "\xc3\x9f"),
1130 ESCAPE_VALUE("τ", "\xce\xa4"),
1131 ESCAPE_VALUE("θ", "\xce\x98"),
1132 ESCAPE_VALUE("þ", "\xc3\xbe"),
1133 ESCAPE_VALUE("×", "\xc3\x97"),
1134 ESCAPE_VALUE("ú", "\xc3\xba"),
1135 ESCAPE_VALUE("↑", "\xe2\x86\x91"),
1136 ESCAPE_VALUE("û", "\xc3\xbb"),
1137 ESCAPE_VALUE("ù", "\xc3\xb9"),
1138 ESCAPE_VALUE("¨", "\xc2\xa8"),
1139 ESCAPE_VALUE("υ", "\xce\xa5"),
1140 ESCAPE_VALUE("ü", "\xc3\xbc"),
1141 ESCAPE_VALUE("ξ", "\xce\x9e"),
1142 ESCAPE_VALUE("ý", "\xc3\xbd"),
1143 ESCAPE_VALUE("¥", "\xc2\xa5"),
1144 ESCAPE_VALUE("ÿ", "\xc3\xbf"),
1145 ESCAPE_VALUE("ζ", "\xce\x96"),
1146 ESCAPE_VALUE("‍", "\xe2\x80\x8d"),
1147 ESCAPE_VALUE("‌", "\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, ¶m[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 (">",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 /* & -> & */
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 /* & -> & */
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, "");
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