/** * @internal * @subsection Evas_Object_Textblock_Internal Internal Textblock Object Tutorial * * This explains the internal design of the Evas Textblock Object, it's assumed * that the reader of this section has already read @ref Evas_Object_Textblock_Tutorial "Textblock's usage docs.". * * @subsection textblock_internal_intro Introduction * There are two main parts to the textblock object, the first being the node * system, and the second being the layout system. The former is just an * internal representation of the markup text, while the latter is the internal * visual representation of the text (i.e positioning, sizing, fonts and etc). * * @subsection textblock_nodes The Nodes system * The nodes mechanism consists of two main data types: * ::Evas_Object_Textblock_Node_Text and ::Evas_Object_Textblock_Node_Format * the former is for Text nodes and the latter is for format nodes. * There's always at least one text node, even if there are only formats. * * @subsection textblock_nodes_text Text nodes * Each text node is essentially a paragraph, it includes an @ref Eina_UStrbuf * that stores the actual paragraph text, a utf8 string to store the paragraph * text in utf8 (which is not used internally at all), A pointer to it's * main @ref textblock_nodes_format_internal "Format Node" and the paragraph's * @ref evas_bidi_props "BiDi properties". The pointer to the format node may be * NULL if there's no format node anywhere before the end of the text node, * not even in previous text nodes. If not NULL, it points to the first format * node pointing to text inside of the text node, or if there is none, it points * to the previous's text nodes format node. Each paragraph has a format node * representing a paragraph separator pointing to it's last position except * for the last paragraph, which has no such constraint. This constraint * happens because text nodes are paragraphs and paragraphs are delimited by * paragraph separators. * * @subsection textblock_nodes_format_internal Format Nodes - Internal * Each format node stores a group of format information, for example the * markup: \ will all be inserted * inside the same format node, although it consists of different formatting * commands. * Each node has a pointer to it's text node, this pointer is NEVER NULL, even * if there's only one format, and no text, a text node is created. Each format * node includes an offset from the last format node of the same text node. For * example, the markup "012" will create two format nodes, the first * having an offset of 1 and the second an offset of 2. Each format node also * includes a @ref Eina_Strbuf that includes the textual representation of the * format, and a boolean stating if the format is a visible format or not, see * @ref textblock_nodes_format_visible * * @subsection textblock_nodes_format_visible Visible Format Nodes * There are two types of format nodes, visible and invisible. They are the same * in every way, except for the representation in the text node. While invisible * format nodes have no representation in the text node, the visible ones do. * The Uniceode object replacement character (0xFFFC) is inserted to every place * a visible format node points to. This makes it very easy to treat visible * formats as items in the text, both for BiDi purposes and cursor handling * purposes. * Here are a few example visible an invisible formats: * Visible: newline char, tab, paragraph separator and an embedded item. * Invisible: setting the color, font or alignment of the text. * * @subsection textblock_layout The layout system * @todo write @ref textblock_layout */ #define EFL_CANVAS_FILTER_INTERNAL_PROTECTED #include "evas_common_private.h" #include "evas_private.h" #include "efl_text_cursor.eo.h" #include "Efl.h" #include "efl_canvas_textblock_internal.h" //#define LYDBG(f, args...) printf(f, ##args) #define LYDBG(f, args...) #define MY_CLASS EFL_CANVAS_TEXTBLOCK_CLASS #define MY_CLASS_NAME "Efl Canvas Text" #include "linebreak.h" #include "wordbreak.h" #include "graphemebreak.h" #include "evas_filter.h" #include "efl_canvas_filter_internal.eo.h" /* private magic number for textblock objects */ static const char o_type[] = "textblock"; /* The char to be inserted instead of visible formats */ #define _REPLACEMENT_CHAR 0xFFFC #define _PARAGRAPH_SEPARATOR 0x2029 #define _NEWLINE '\n' #define _TAB '\t' #define EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(ch) \ (((ch) == _REPLACEMENT_CHAR) || \ ((ch) == _NEWLINE) || \ ((ch) == _TAB) || \ ((ch) == _PARAGRAPH_SEPARATOR)) #ifdef CRI #undef CRI #endif #define CRI(...) EINA_LOG_DOM_CRIT(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__) #ifdef ERR #undef ERR #endif #define ERR(...) EINA_LOG_DOM_ERR(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__) #ifdef WRN #undef WRN #endif #define WRN(...) EINA_LOG_DOM_WARN(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__) #ifdef INF #undef INF #endif #define INF(...) EINA_LOG_DOM_INFO(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__) #ifdef DBG #undef DBG #endif #define DBG(...) EINA_LOG_DOM_DBG(EINA_LOG_DOMAIN_DEFAULT, __VA_ARGS__) #define TB_NULL_CHECK(null_check, ...) \ do \ { \ if (!null_check) \ { \ ERR("%s is NULL while it shouldn't be, please notify developers.", #null_check); \ return __VA_ARGS__; \ } \ } \ while(0) // testing out some macros to maybe add to eina #define EINA_INLIST_REMOVE(l,i) do { l = (__typeof__(l)) eina_inlist_remove(EINA_INLIST_GET(l), EINA_INLIST_GET(i)); } while (0) #define EINA_INLIST_APPEND(l,i) do { l = (__typeof__(l)) eina_inlist_append(EINA_INLIST_GET(l), EINA_INLIST_GET(i)); } while (0) /** * @internal * @typedef TEXT_FIT_CONTENT_CONFIG * Configurations used to fit content inside Textblock */ typedef struct _TEXT_FIT_CONTENT_CONFIG TEXT_FIT_CONTENT_CONFIG; /** * @internal * @def IS_AT_END(ti, ind) * Return true if ind is at the end of the text item, false otherwise. */ #define IS_AT_END(ti, ind) (ind == ti->text_props.text_len) /** * @internal * @def MOVE_PREV_UNTIL(limit, ind) * This decrements ind as long as ind > limit. */ #define MOVE_PREV_UNTIL(limit, ind) \ do \ { \ if ((limit) < (ind)) \ (ind)--; \ } \ while (0) /** * @internal * @def MOVE_NEXT_UNTIL(limit, ind) * This increments ind as long as ind < limit */ #define MOVE_NEXT_UNTIL(limit, ind) \ do \ { \ if ((ind) < (limit)) \ (ind)++; \ } \ while (0) /** * @internal * @def GET_ITEM_LEN(it) * Returns length of item (Format or Text) */ #define GET_ITEM_LEN(it) \ (((it)->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? \ _ITEM_TEXT(it)->text_props.text_len : 1) /** * @internal * @def GET_ITEM_TEXT(ti) * Returns a const reference to the text of the ti (not null terminated). */ #define GET_ITEM_TEXT(ti) \ (((ti)->parent.text_node) ? \ (eina_ustrbuf_string_get((ti)->parent.text_node->unicode) + \ (ti)->parent.text_pos) : EINA_UNICODE_EMPTY_STRING) /** * @internal * @def _FORMAT_IS_CLOSER_OF(base, closer, closer_len) * Returns true if closer is the closer of base. */ #define _FORMAT_IS_CLOSER_OF(base, closer, closer_len) \ (!strncmp(base, closer, closer_len) && \ (!base[closer_len] || \ (base[closer_len] == '=') || \ _is_white(base[closer_len]))) /* The default tags to use */ static const Evas_Object_Style_Tag_Base default_tags[] = { { "b", "+ font_weight=Bold", 1 }, { "i", "+ font_style=Italic", 1 }}; #define ANCHOR_NONE 0 #define ANCHOR_A 1 #define ANCHOR_ITEM 2 /** * @internal * @def _NODE_TEXT(x) * A convenience macro for casting to a text node. */ #define _NODE_TEXT(x) ((Evas_Object_Textblock_Node_Text *) (x)) /** * @internal * @def _NODE_FORMAT(x) * A convenience macro for casting to a format node. */ #define _NODE_FORMAT(x) ((Evas_Object_Textblock_Node_Format *) (x)) /** * @internal * @def _ITEM(x) * A convenience macro for casting to a generic item. */ #define _ITEM(x) ((Evas_Object_Textblock_Item *) (x)) /** * @internal * @def _ITEM_TEXT(x) * A convenience macro for casting to a text item. */ #define _ITEM_TEXT(x) ((Evas_Object_Textblock_Text_Item *) (x)) /** * @internal * @def _ITEM_FORMAT(x) * A convenience macro for casting to a format item. */ #define _ITEM_FORMAT(x) ((Evas_Object_Textblock_Format_Item *) (x)) struct _Evas_Object_Textblock_Paragraph { EINA_INLIST; Evas_Object_Textblock_Line *lines; /**< Points to the first line of this paragraph. */ Evas_Object_Textblock_Node_Text *text_node; /**< Points to the first text node of this paragraph. */ Eina_List *logical_items; /**< Logical items are the properties of this paragraph, like width, height etc. */ Evas_BiDi_Paragraph_Props *bidi_props; /**< Only valid during layout. */ Evas_BiDi_Direction direction; /**< Bidi direction enum value. The display direction like right to left.*/ Evas_Coord y, w, h; /**< Text block co-ordinates. y co-ord, width and height. */ Evas_Coord last_fw; /**< Last calculated formatted width */ int line_no; /**< Line no of the text block. */ Eina_Bool is_bidi : 1; /**< EINA_TRUE if this is BiDi Paragraph, else EINA_FALSE. */ Eina_Bool visible : 1; /**< EINA_TRUE if paragraph visible, else EINA_FALSE. */ Eina_Bool rendered : 1; /**< EINA_TRUE if paragraph rendered, else EINA_FALSE. */ }; struct _Evas_Object_Textblock_Line { EINA_INLIST; Evas_Object_Textblock_Item *items; /**< Pointer to layouting text item. Contains actual text and information about its display. */ Evas_Object_Textblock_Paragraph *par; /**< Points to the paragraph of which this line is a part. */ Evas_Coord x, y, w, h; /**< Text block line co-ordinates. */ int baseline; /**< Baseline of the textblock. */ int line_no; /**< Line no of this line. */ }; typedef enum _Evas_Textblock_Item_Type { EVAS_TEXTBLOCK_ITEM_TEXT, EVAS_TEXTBLOCK_ITEM_FORMAT, } Evas_Textblock_Item_Type; typedef enum _Evas_Textblock_Align_Auto { EVAS_TEXTBLOCK_ALIGN_AUTO_NONE, EVAS_TEXTBLOCK_ALIGN_AUTO_NORMAL, EVAS_TEXTBLOCK_ALIGN_AUTO_LOCALE, EVAS_TEXTBLOCK_ALIGN_AUTO_END } Evas_Textblock_Align_Auto; struct _Evas_Object_Textblock_Item { EINA_INLIST; Evas_Object_Textblock_Node_Text *text_node; /**< Pointer to textblock node text. It contains actual text in unicode and utf8 format. */ Evas_Object_Textblock_Format *format; /**< Pointer to textblock format. It contains all the formatting information for this text block. */ Evas_Object_Textblock_Line *ln; /**< Pointer to textblock line. It contains the co-ord, baseline, and line no for this item. */ size_t text_pos; /**< Position of this item in textblock line. */ #ifdef BIDI_SUPPORT size_t visual_pos; /**< Visual position of this item. */ #endif Evas_Textblock_Item_Type type; /**< EVAS_TEXTBLOCK_ITEM_TEXT or EVAS_TEXTBLOCK_ITEM_FORMAT */ Evas_Coord adv, x, w, h; /**< Item co-ordinates. Advancement to be made, x co-ord, width and height. */ Evas_Coord yoff; /**< y offset. */ Eina_Bool merge : 1; /**< Indicates whether this item should merge to the previous item or not */ Eina_Bool visually_deleted : 1; /**< Indicates whether this item is used in the visual layout or not. */ }; struct _Evas_Object_Textblock_Text_Item { Evas_Object_Textblock_Item parent; /**< Textblock item. */ Evas_Text_Props text_props; /**< Props for this item. */ Evas_Coord x_adjustment; /**< Used to indicate by how much we adjusted sizes */ Text_Item_Filter *gfx_filter; }; struct _Evas_Object_Textblock_Format_Item { Evas_Object_Textblock_Item parent; /**< Textblock item. */ Evas_BiDi_Direction bidi_dir; /**< Bidi text direction. */ const char *item; /**< Pointer to item contents. */ int y; /**< Co-ordinate of item. */ unsigned char vsize : 2; /**< VSIZE_FULL or VSIZE_ASCENT */ unsigned char size : 2; /**< SIZE, SIZE_ABS or SIZE_REL*/ Eina_Bool formatme : 1; /**< EINA_TRUE if format required, else EINA_FALSE */ }; struct _Text_Item_Filter { EINA_INLIST; /**< list on the tb object */ Efl_Canvas_Textblock_Data *textblock; Evas_Object_Textblock_Text_Item *ti; /**< associated text item. if null, it was deleted */ Evas_Filter_Context *ctx; /**< running context for the filter */ Evas_Public_Data *evas; /**< evas instance */ void *output; /**< output rgba buffer for this text item (engine image) */ Eina_Bool do_async; /**< do_async flag when running the filter */ }; struct _Efl_Canvas_Textblock_Filter { Eina_Stringshare *name; Evas_Object *eo_obj; Evas_Public_Data *evas; void *dc; /* draw context - no clip, white, no colmul... */ Evas_Filter_Padding pad; Eina_Bool invalid; Eina_Bool redraw; }; struct _Efl_Canvas_Textblock_Filter_Post_Render { Evas_Filter_Context *ctx; Eina_Bool success; }; struct _Efl_Canvas_Textblock_Filter_Program { EINA_INLIST; Eina_Stringshare *name; Eina_Stringshare *code; Evas_Filter_Program *pgm; Eina_Bool changed; }; struct _Evas_Object_Textblock_Format { Evas_Object_Textblock_Node_Format *fnode; /**< Pointer to textblock format node. */ double halign; /**< Horizontal alignment value. */ double valign; /**< Vertical alignment value. */ struct { Evas_Font_Description *fdesc; /**< Pointer to font description. */ const char *source; /**< Pointer to object from which to search for the font. */ Evas_Font_Set *font; /**< Pointer to font set. */ Evas_Font_Size size; /**< Size of the font. */ Efl_Text_Font_Bitmap_Scalable bitmap_scalable; /**< Scalable for bitmap font. */ } font; struct { struct { unsigned char r, g, b, a; } normal, underline, underline2, underline_dash, outline, shadow, glow, glow2, backing, strikethrough; } color; struct { int l, r; } margin; /**< Left and right margin width. */ Efl_Canvas_Textblock_Filter *gfx_filter; /**< Gfx Filter to apply to the children text items */ int ref; /**< Value of the ref. */ int tabstops; /**< Value of the size of the tab character. */ int linesize; /**< Value of the size of the line of the text. */ int linegap; /**< Value to set the line gap in text. */ int underline_dash_width; /**< Valule to set the width of the underline dash. */ int underline_dash_gap; /**< Value to set the gap of the underline dash. */ double underline_height; /**< Value to set the height of the single underline. */ double linerelsize; /**< Value to set the size of line of text. */ double linerelgap; /**< Value for setting line gap. */ double linefill; /**< The value must be a percentage. */ 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. */ unsigned char style; /**< Value from Evas_Text_Style_Type enum. */ Eina_Bool wrap_word : 1; /**< EINA_TRUE if only wraps lines at word boundaries, else EINA_FALSE. */ Eina_Bool wrap_char : 1; /**< EINA_TRUE if wraps at any character, else EINA_FALSE. */ Eina_Bool wrap_mixed : 1; /**< EINA_TRUE if wrap at words if possible, else EINA_FALSE. */ Eina_Bool wrap_hyphenation : 1; /**< EINA_TRUE if wrap at mixed and hyphenate if possible, else EINA_FALSE. */ Eina_Bool underline : 1; /**< EINA_TRUE if a single line under the text, else EINA_FALSE */ Eina_Bool underline2 : 1; /**< EINA_TRUE if two lines under the text, else EINA_FALSE */ Eina_Bool underline_dash : 1; /**< EINA_TRUE if a dashed line under the text, else EINA_FALSE */ Eina_Bool strikethrough : 1; /**< EINA_TRUE if text should be stricked off, else EINA_FALSE */ Eina_Bool backing : 1; /**< EINA_TRUE if enable background color, else EINA_FALSE */ Eina_Bool password : 1; /**< EINA_TRUE if the text is password, else EINA_FALSE */ Evas_Textblock_Align_Auto halign_auto : 2; /**< Auto horizontal align mode */ }; struct _Efl_Canvas_Textblock_Style { const char *style_text; const char *default_tag; Evas_Object_Style_Tag *tags; Eina_List *objects; Eina_Bool delete_me : 1; Eina_Bool legacy : 1; }; typedef struct _User_Style_Entry { Evas_Textblock_Style *st; const char *key; } User_Style_Entry; struct _TEXT_FIT_CONTENT_CONFIG { unsigned int options; unsigned int min_font_size,max_font_size; unsigned int step_size; unsigned int *p_size_array; size_t size_list_length; Eina_Size2D size_cache[256+1]; /** used hash font sizes 1-255 */ Eina_Size2D last_size; int last_size_index; Eina_Bool force_refit; char fit_style[256]; }; #define _FMT(x) (o->default_format.format.x) #define _FMT_INFO(x) (o->default_format.info.x) /* Size of the index array */ #define TEXTBLOCK_PAR_INDEX_SIZE 10 #define ASYNC_BLOCK do { \ if (o->layout_th) \ { \ ecore_thread_wait(o->layout_th, 1); \ }} while(0) #include "Ecore.h" struct _Evas_Object_Textblock { Ecore_Thread *layout_th; int layout_jobs; Evas_Textblock_Style *style; Eina_List *styles; Efl_Text_Cursor_Handle *cursor; Eina_List *cursors; Evas_Object_Textblock_Node_Text *text_nodes; Evas_Object_Textblock_Node_Format *format_nodes; int num_paragraphs; Evas_Object_Textblock_Paragraph *paragraphs; Evas_Object_Textblock_Paragraph *par_index[TEXTBLOCK_PAR_INDEX_SIZE]; Evas_Object_Textblock_Text_Item *ellip_ti; Eina_List *anchors_a; Eina_List *anchors_item; Eina_List *obstacles; Eina_List *hyphen_items; /* Hyphen items storage to free when clearing lines */ Efl_Text_Attribute_Handle *annotations; /* All currently applied annotations on the text. */ int last_w, last_h; struct { int l, r, t, b; } style_pad; struct { Evas_Object_Textblock_Format format; struct { Eina_Stringshare *font; Evas_Font_Size size; Eina_Stringshare *font_source; Eina_Stringshare *font_fallbacks; Eina_Stringshare *font_lang; Eina_Stringshare *gfx_filter_name; unsigned int font_weight; unsigned int font_slant; unsigned int font_width; Efl_Text_Style_Effect_Type effect; Efl_Text_Style_Shadow_Direction shadow_direction; Efl_Text_Format_Wrap wrap; Efl_Text_Font_Bitmap_Scalable bitmap_scalable; } info; char * default_style_str; } default_format; double valign; Eina_Stringshare *markup_text; char *utf8; void *engine_data; const char *repch; const char *bidi_delimiters; Evas_BiDi_Direction paragraph_direction : 2; struct { int w, h, oneline_h; Eina_Bool valid : 1; } formatted, native; struct { Efl_Canvas_Textblock_Filter_Program *programs; Evas_Filter_Data_Binding *data_bindings; Eina_Hash *sources; Text_Item_Filter *text_items; // inlist } gfx_filter; TEXT_FIT_CONTENT_CONFIG fit_content_config; Eina_Bool redraw : 1; Eina_Bool changed : 1; Eina_Bool pause_change : 1; Eina_Bool obstacle_changed : 1; Eina_Bool content_changed : 1; Eina_Bool format_changed : 1; Eina_Bool have_ellipsis : 1; Eina_Bool hyphenating : 1; Eina_Bool legacy_newline : 1; Eina_Bool inherit_paragraph_direction : 1; Eina_Bool changed_paragraph_direction : 1; Eina_Bool multiline : 1; Eina_Bool wrap_changed : 1; Eina_Bool auto_styles : 1; Eina_Bool fit_in_progress : 1; Eina_Bool is_legacy : 1; }; struct _Evas_Textblock_Selection_Iterator { Eina_Iterator iterator; /**< Eina Iterator. */ Eina_List *list; /**< Head of list. */ Eina_List *current; /**< Current node in loop. */ }; struct _Efl_Text_Attribute_Handle_Iterator { Eina_Iterator iterator; /**< Eina Iterator. */ Eina_List *list; /**< Head of list. */ Eina_List *current; /**< Current node in loop. */ }; /* private methods for textblock objects */ static void evas_object_textblock_init(Evas_Object *eo_obj); static void evas_object_textblock_render(Evas_Object *eo_obj, 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); static void evas_object_textblock_free(Evas_Object *eo_obj); static void evas_object_textblock_render_pre(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, void *type_private_data); static void evas_object_textblock_render_post(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, void *type_private_data); static Evas_Object_Textblock_Node_Text *_evas_textblock_node_text_new(void); static void *evas_object_textblock_engine_data_get(Evas_Object *eo_obj); static int evas_object_textblock_is_opaque(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, void *type_private_data); static int evas_object_textblock_was_opaque(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, void *type_private_data); static void evas_object_textblock_coords_recalc(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, void *type_private_data); static void _canvas_text_format_changed(Eo *eo_obj, Efl_Canvas_Textblock_Data *o); static const Evas_Object_Func object_func = { /* methods (compulsory) */ NULL, evas_object_textblock_render, evas_object_textblock_render_pre, evas_object_textblock_render_post, evas_object_textblock_engine_data_get, /* these are optional. NULL = nothing */ NULL, NULL, evas_object_textblock_is_opaque, evas_object_textblock_was_opaque, NULL, NULL, NULL, /*evas_object_textblock_coords_recalc, <- disable - not useful. */ NULL, NULL, NULL, NULL // render_prepare }; /* the actual api call to add a textblock */ #define TB_HEAD() \ MAGIC_CHECK(eo_obj, Evas_Object, MAGIC_OBJ); \ return; \ MAGIC_CHECK_END(); \ Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS); #define TB_HEAD_RETURN(x) \ MAGIC_CHECK(eo_obj, Evas_Object, MAGIC_OBJ); \ return (x); \ MAGIC_CHECK_END(); static Eina_Bool _evas_textblock_cursor_is_at_the_end(const Efl_Text_Cursor_Handle *cur); static void _evas_textblock_node_text_remove(Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Node_Text *n); static Evas_Object_Textblock_Node_Format *_evas_textblock_cursor_node_format_before_or_at_pos_get(const Efl_Text_Cursor_Handle *cur); static size_t _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt); static void _evas_textblock_node_format_remove(Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Node_Format *n, int visual_adjustment); static void _evas_textblock_node_format_free(Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Node_Format *n); static void _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n); static void _evas_textblock_changed(Efl_Canvas_Textblock_Data *o, Evas_Object *eo_obj); static void _evas_textblock_invalidate_all(Efl_Canvas_Textblock_Data *o); 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); 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); static Eina_Bool _evas_textblock_cursor_format_is_visible_get(const Efl_Text_Cursor_Handle *cur); static void _evas_textblock_cursor_at_format_set(Efl_Text_Cursor_Handle *cur, const Evas_Object_Textblock_Node_Format *fmt); static Evas_Filter_Program *_format_filter_program_get(Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Format *fmt); 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); #ifdef HAVE_HYPHEN /* Hyphenation */ #include "evas_textblock_hyphenation.x" #endif 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); EAPI Eina_Bool evas_textblock_cursor_eol_get(const Evas_Textblock_Cursor *cur); static Eina_Bool _evas_textblock_cursor_format_is_visible_get(const Efl_Text_Cursor_Handle *cur); 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); static Evas_Object_Textblock_Node_Format *_evas_textblock_cursor_node_format_at_pos_get(const Efl_Text_Cursor_Handle *cur); static int _evas_textblock_cursor_text_prepend(Efl_Text_Cursor_Handle *cur, const char *_text); static void _evas_textblock_cursor_copy(Efl_Text_Cursor_Handle *dst, const Efl_Text_Cursor_Handle *src); static void _textblock_style_generic_set(Evas_Object *eo_obj, Evas_Textblock_Style *ts, const char *key); /*********Internal fitting Functions and Defines*********/ int fit_cache_clear(TEXT_FIT_CONTENT_CONFIG *fc,const unsigned int fit_cache_flags); int fit_text_block(Evas_Object *eo_obj); int fit_fill_internal_list(TEXT_FIT_CONTENT_CONFIG *fc); int fit_start_fitting(Evas_Object *eo_obj); int fit_finish_fitting(Evas_Object *eo_obj); Eina_Bool fit_is_fitting(const Evas_Object *eo_obj); const unsigned int FIT_CACHE_CANVAS_SIZE = 0x0001; const unsigned int FIT_CACHE_INTERNAL_SIZE_ARRAY = 0x0002; const unsigned int FIT_CACHE_FORCE_REFIT = 0x0004; const unsigned int FIT_CACHE_ALL = 0x000F; /** selection iterator */ /** * @internal * Returns the value of the current data of list node, * and goes to the next list node. * * @param it the iterator. * @param data the data of the current list node. * @return EINA_FALSE if the current list node does not exists. * Otherwise, returns EINA_TRUE. */ static Eina_Bool _evas_textblock_selection_iterator_next(Evas_Textblock_Selection_Iterator *it, void **data) { if (!it->current) return EINA_FALSE; *data = eina_list_data_get(it->current); it->current = eina_list_next(it->current); return EINA_TRUE; } /** * @internal * Gets the iterator container (Eina_List) which created the iterator. * @param it the iterator. * @return A pointer to Eina_List. */ static Eina_List * _evas_textblock_selection_iterator_get_container(Evas_Textblock_Selection_Iterator *it) { return it->list; } /** * @internal * Frees the iterator container (Eina_List). * @param it the iterator. */ static void _evas_textblock_selection_iterator_free(Evas_Textblock_Selection_Iterator *it) { Evas_Textblock_Rectangle *tr; EINA_LIST_FREE(it->list, tr) free(tr); EINA_MAGIC_SET(&it->iterator, 0); free(it); } /** * @internal * Creates newly allocated iterator associated to a list. * @param list The list. * @return If the memory cannot be allocated, NULL is returned. * Otherwise, a valid iterator is returned. */ Eina_Iterator * _evas_textblock_selection_iterator_new(Eina_List *list) { Evas_Textblock_Selection_Iterator *it; it = calloc(1, sizeof(Evas_Textblock_Selection_Iterator)); if (!it) return NULL; EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); it->list = list; it->current = list; it->iterator.version = EINA_ITERATOR_VERSION; it->iterator.next = FUNC_ITERATOR_NEXT( _evas_textblock_selection_iterator_next); it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( _evas_textblock_selection_iterator_get_container); it->iterator.free = FUNC_ITERATOR_FREE( _evas_textblock_selection_iterator_free); return &it->iterator; } /* styles */ /** * @internal * Clears the textblock style passed except for the style_text which is replaced. * @param ts The ts to be cleared. Must not be NULL. * @param style_text the style's text. */ static void _style_replace(Evas_Textblock_Style *ts, const char *style_text) { eina_stringshare_replace(&ts->style_text, style_text); if (ts->default_tag) eina_stringshare_del(ts->default_tag); while (ts->tags) { Evas_Object_Style_Tag *tag; tag = (Evas_Object_Style_Tag *)ts->tags; ts->tags = (Evas_Object_Style_Tag *)eina_inlist_remove(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag)); eina_stringshare_del(tag->tag.tag); eina_stringshare_del(tag->tag.replace); free(tag); } ts->default_tag = NULL; ts->tags = NULL; } /** * @internal * Clears the textblock style passed. * @param ts The ts to be cleared. Must not be NULL. */ static void _style_clear(Evas_Textblock_Style *ts) { _style_replace(ts, NULL); } /** * @internal * Searches inside the tags stored in the style for the tag matching s. * @param ts The ts to be cleared. Must not be NULL. * @param s The tag to be matched. * @param tag_len the length of the tag string. * @param[out] replace_len The length of the replacement found. - Must not be NULL. * @return The replacement string found. */ static inline const char * _style_match_tag(const Evas_Textblock_Style *ts, const char *s, size_t tag_len) { Evas_Object_Style_Tag *tag; /* Try the style tags */ if (ts) { EINA_INLIST_FOREACH(ts->tags, tag) { if (tag->tag.tag_len != tag_len) continue; if (!strncmp(tag->tag.tag, s, tag_len)) { return tag->tag.replace; } } } /* Try the default tags */ { size_t i; const Evas_Object_Style_Tag_Base *btag; for (btag = default_tags, i = 0 ; i < (sizeof(default_tags) / sizeof(default_tags[0])) ; btag++, i++) { if (btag->tag_len != tag_len) continue; if (!strncmp(btag->tag, s, tag_len)) { return btag->replace; } } } return NULL; } /** * @internal * Clears all the nodes (text and format) of the textblock object. * @param obj The evas object, must not be NULL. */ static void _nodes_clear(const Evas_Object *eo_obj) { Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS); /* First, clear all annotations that may have spawned format nodes. */ _evas_textblock_annotations_clear(eo_obj); while (o->text_nodes) { Evas_Object_Textblock_Node_Text *n; n = o->text_nodes; o->text_nodes = _NODE_TEXT(eina_inlist_remove( EINA_INLIST_GET(o->text_nodes), EINA_INLIST_GET(n))); _evas_textblock_node_text_free(n); } while (o->format_nodes) { Evas_Object_Textblock_Node_Format *n; n = o->format_nodes; o->format_nodes = _NODE_FORMAT(eina_inlist_remove(EINA_INLIST_GET(o->format_nodes), EINA_INLIST_GET(n))); _evas_textblock_node_format_free(o, n); } } /** * @internal * Unrefs and frees (if needed) a textblock format. * @param obj The Evas_Object, Must not be NULL. * @param fmt the format to be cleaned, must not be NULL. */ static void _format_unref_free(Evas_Object_Protected_Data *evas_o, Evas_Object_Textblock_Format *fmt) { Evas_Object_Protected_Data *obj = evas_o; fmt->ref--; if (fmt->ref > 0) return; if (fmt->font.fdesc) evas_font_desc_unref(fmt->font.fdesc); if (fmt->font.source) eina_stringshare_del(fmt->font.source); if (fmt->gfx_filter) { eina_stringshare_del(fmt->gfx_filter->name); if (fmt->gfx_filter->dc) ENFN->context_free(ENC, fmt->gfx_filter->dc); free(fmt->gfx_filter); fmt->gfx_filter = NULL; } if ((obj->layer) && (obj->layer->evas)) evas_font_free(fmt->font.font); free(fmt); } static inline void _image_safe_unref(Evas_Public_Data *e, void *image, Eina_Bool async) { if (!image) return; if (async) evas_unref_queue_image_put(e, image); else e->engine.func->image_free(_evas_engine_context(e), image); } /** * @internal * Free a layout item * @param obj The evas object, must not be NULL. * @param ln the layout line on which the item is in, must not be NULL. * @param it the layout item to be freed */ static void _item_free(Evas_Public_Data *evas, Efl_Canvas_Textblock_Data *o, Evas_Object_Protected_Data *evas_o, Evas_Object_Textblock_Line *ln, Evas_Object_Textblock_Item *it) { if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) { Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it); evas_common_text_props_content_unref(&ti->text_props); if (EINA_UNLIKELY(ti->gfx_filter != NULL)) { if (ti->gfx_filter->output) { //Evas *eo_evas = evas_object_evas_get(eo_obj); //Evas_Public_Data *evas = efl_data_scope_get(eo_evas, EVAS_CANVAS_CLASS); Eina_Bool async = ti->gfx_filter->do_async; _image_safe_unref(evas, ti->gfx_filter->output, async); ti->gfx_filter->output = NULL; } EINA_INLIST_REMOVE(o->gfx_filter.text_items, ti->gfx_filter); if (!ti->gfx_filter->ctx) { free(ti->gfx_filter); ti->gfx_filter = NULL; } else { evas_filter_context_unref(ti->gfx_filter->ctx); ti->gfx_filter->ctx = NULL; ti->gfx_filter->ti = NULL; } } } else { Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it); if (fi->item) eina_stringshare_del(fi->item); } _format_unref_free(evas_o, it->format); if (ln) { ln->items = (Evas_Object_Textblock_Item *) eina_inlist_remove( EINA_INLIST_GET(ln->items), EINA_INLIST_GET(it)); } free(it); } /** * @internal * Free a layout line. * @param obj The evas object, must not be NULL. * @param ln the layout line to be freed, must not be NULL. */ static void _line_free(Evas_Object_Textblock_Line *ln) { /* Items are freed from the logical list, except for the ellip item */ if (ln) free(ln); } /* table of html escapes (that i can find) this should be ordered with the * sorted by there escape strings and values as it's a binary search to match - no hash for this. * * these are stored as array of struct of Escape_Value structure (no Runtime sort will happen) */ /** * @internal * @var escape_values_e_sorted[] * This array consists of Escape_Value structure sorted by escape string * And new added value must be placed sorted position, and reflected on escape_values_v_sorted */ typedef struct _Escape_Value Escape_Value; struct _Escape_Value { char *escape; char *value; size_t escape_len; size_t value_len; }; #define ESCAPE_VALUE(e,v) {e,v,strlen(e),strlen(v)} static const Escape_Value escape_values_e_sorted[] = { ESCAPE_VALUE("Á", "\xc3\x81"), ESCAPE_VALUE("Â", "\xc3\x82"), ESCAPE_VALUE("&Aelig;", "\xc3\x86"), ESCAPE_VALUE("À", "\xc3\x80"), ESCAPE_VALUE("Å", "\xc3\x85"), ESCAPE_VALUE("Ã", "\xc3\x83"), ESCAPE_VALUE("Ä", "\xc3\x84"), ESCAPE_VALUE("Ç", "\xc3\x87"), ESCAPE_VALUE("‡", "\xe2\x80\xa1"), ESCAPE_VALUE("É", "\xc3\x89"), ESCAPE_VALUE("Ê", "\xc3\x8a"), ESCAPE_VALUE("È", "\xc3\x88"), ESCAPE_VALUE("&Eth;", "\xc3\x90"), ESCAPE_VALUE("Ë", "\xc3\x8b"), ESCAPE_VALUE("Í", "\xc3\x8d"), ESCAPE_VALUE("Î", "\xc3\x8e"), ESCAPE_VALUE("Ì", "\xc3\x8c"), ESCAPE_VALUE("Ï", "\xc3\x8f"), ESCAPE_VALUE("Ñ", "\xc3\x91"), ESCAPE_VALUE("Ó", "\xc3\x93"), ESCAPE_VALUE("Ô", "\xc3\x94"), ESCAPE_VALUE("Ò", "\xc3\x92"), ESCAPE_VALUE("Ø", "\xc3\x98"), ESCAPE_VALUE("Õ", "\xc3\x95"), ESCAPE_VALUE("Ö", "\xc3\x96"), ESCAPE_VALUE("&Thorn;", "\xc3\x9e"), ESCAPE_VALUE("Ú", "\xc3\x9a"), ESCAPE_VALUE("Û", "\xc3\x9b"), ESCAPE_VALUE("Ù", "\xc3\x99"), ESCAPE_VALUE("Ý", "\xc3\x9d"), ESCAPE_VALUE("á", "\xc3\xa1"), ESCAPE_VALUE("â", "\xc3\xa2"), ESCAPE_VALUE("´", "\xc2\xb4"), ESCAPE_VALUE("æ", "\xc3\xa6"), ESCAPE_VALUE("à", "\xc3\xa0"), ESCAPE_VALUE("α", "\xce\x91"), ESCAPE_VALUE("∧", "\xe2\x88\xa7"), ESCAPE_VALUE("å", "\xc3\xa5"), ESCAPE_VALUE("ã", "\xc3\xa3"), ESCAPE_VALUE("ä", "\xc3\xa4"), ESCAPE_VALUE("β", "\xce\x92"), ESCAPE_VALUE("¦", "\xc2\xa6"), ESCAPE_VALUE("•", "\xe2\x80\xa2"), ESCAPE_VALUE("ç", "\xc3\xa7"), ESCAPE_VALUE("¸", "\xc2\xb8"), ESCAPE_VALUE("¢", "\xc2\xa2"), ESCAPE_VALUE("χ", "\xce\xa7"), ESCAPE_VALUE("©", "\xc2\xa9"), ESCAPE_VALUE("¤", "\xc2\xa4"), ESCAPE_VALUE("†", "\xe2\x80\xa0"), ESCAPE_VALUE("↓", "\xe2\x86\x93"), ESCAPE_VALUE("°", "\xc2\xb0"), ESCAPE_VALUE("δ", "\xce\x94"), ESCAPE_VALUE("÷", "\xc3\xb7"), ESCAPE_VALUE("é", "\xc3\xa9"), ESCAPE_VALUE("ê", "\xc3\xaa"), ESCAPE_VALUE("è", "\xc3\xa8"), ESCAPE_VALUE("ε", "\xce\x95"), ESCAPE_VALUE("≡", "\xe2\x89\xa1"), ESCAPE_VALUE("η", "\xce\x97"), ESCAPE_VALUE("ð", "\xc3\xb0"), ESCAPE_VALUE("ë", "\xc3\xab"), ESCAPE_VALUE("€", "\xe2\x82\xac"), ESCAPE_VALUE("∃", "\xe2\x88\x83"), ESCAPE_VALUE("∀", "\xe2\x88\x80"), ESCAPE_VALUE("½", "\xc2\xbd"), ESCAPE_VALUE("¼", "\xc2\xbc"), ESCAPE_VALUE("¾", "\xc2\xbe"), ESCAPE_VALUE("γ", "\xce\x93"), ESCAPE_VALUE("↔", "\xe2\x86\x94"), ESCAPE_VALUE("…", "\xe2\x80\xa6"), ESCAPE_VALUE("í", "\xc3\xad"), ESCAPE_VALUE("î", "\xc3\xae"), ESCAPE_VALUE("¡", "\xc2\xa1"), ESCAPE_VALUE("ì", "\xc3\xac"), ESCAPE_VALUE("∫", "\xe2\x88\xab"), ESCAPE_VALUE("ι", "\xce\x99"), ESCAPE_VALUE("¿", "\xc2\xbf"), ESCAPE_VALUE("ï", "\xc3\xaf"), ESCAPE_VALUE("κ", "\xce\x9a"), ESCAPE_VALUE("λ", "\xce\x9b"), ESCAPE_VALUE("«", "\xc2\xab"), ESCAPE_VALUE("←", "\xe2\x86\x90"), ESCAPE_VALUE("←", "\xe2\x87\x90"), ESCAPE_VALUE("‎", "\xe2\x80\x8e"), ESCAPE_VALUE("¯", "\xc2\xaf"), ESCAPE_VALUE("µ", "\xc2\xb5"), ESCAPE_VALUE("·", "\xc2\xb7"), ESCAPE_VALUE("μ", "\xce\x9c"), ESCAPE_VALUE("∇", "\xe2\x88\x87"), ESCAPE_VALUE(" ", "\xc2\xa0"), ESCAPE_VALUE("≠", "\xe2\x89\xa0"), ESCAPE_VALUE("¬", "\xc2\xac"), ESCAPE_VALUE("ñ", "\xc3\xb1"), ESCAPE_VALUE("ν", "\xce\x9d"), ESCAPE_VALUE("ó", "\xc3\xb3"), ESCAPE_VALUE("ô", "\xc3\xb4"), ESCAPE_VALUE("ò", "\xc3\xb2"), ESCAPE_VALUE("ω", "\xce\xa9"), ESCAPE_VALUE("ο", "\xce\x9f"), ESCAPE_VALUE("⊕", "\xe2\x8a\x95"), ESCAPE_VALUE("∨", "\xe2\x88\xa8"), ESCAPE_VALUE("ª", "\xc2\xaa"), ESCAPE_VALUE("º", "\xc2\xba"), ESCAPE_VALUE("ø", "\xc3\xb8"), ESCAPE_VALUE("õ", "\xc3\xb5"), ESCAPE_VALUE("ö", "\xc3\xb6"), ESCAPE_VALUE("¶", "\xc2\xb6"), ESCAPE_VALUE("⊥", "\xe2\x8a\xa5"), ESCAPE_VALUE("φ", "\xce\xa6"), ESCAPE_VALUE("π", "\xce\xa0"), ESCAPE_VALUE("±", "\xc2\xb1"), ESCAPE_VALUE("£", "\xc2\xa3"), ESCAPE_VALUE("∏", "\xe2\x88\x8f"), ESCAPE_VALUE("ψ", "\xce\xa8"), ESCAPE_VALUE("»", "\xc2\xbb"), ESCAPE_VALUE("→", "\xe2\x86\x92"), ESCAPE_VALUE("→", "\xe2\x87\x92"), ESCAPE_VALUE("®", "\xc2\xae"), ESCAPE_VALUE("ρ", "\xce\xa1"), ESCAPE_VALUE("‏", "\xe2\x80\x8f"), ESCAPE_VALUE("§", "\xc2\xa7"), ESCAPE_VALUE("­", "\xc2\xad"), ESCAPE_VALUE("σ", "\xce\xa3"), ESCAPE_VALUE("∑", "\xe2\x88\x91"), ESCAPE_VALUE("¹", "\xc2\xb9"), ESCAPE_VALUE("²", "\xc2\xb2"), ESCAPE_VALUE("³", "\xc2\xb3"), ESCAPE_VALUE("ß", "\xc3\x9f"), ESCAPE_VALUE("τ", "\xce\xa4"), ESCAPE_VALUE("θ", "\xce\x98"), ESCAPE_VALUE("þ", "\xc3\xbe"), ESCAPE_VALUE("×", "\xc3\x97"), ESCAPE_VALUE("ú", "\xc3\xba"), ESCAPE_VALUE("↑", "\xe2\x86\x91"), ESCAPE_VALUE("û", "\xc3\xbb"), ESCAPE_VALUE("ù", "\xc3\xb9"), ESCAPE_VALUE("¨", "\xc2\xa8"), ESCAPE_VALUE("υ", "\xce\xa5"), ESCAPE_VALUE("ü", "\xc3\xbc"), ESCAPE_VALUE("ξ", "\xce\x9e"), ESCAPE_VALUE("ý", "\xc3\xbd"), ESCAPE_VALUE("¥", "\xc2\xa5"), ESCAPE_VALUE("ÿ", "\xc3\xbf"), ESCAPE_VALUE("ζ", "\xce\x96"), ESCAPE_VALUE("‍", "\xe2\x80\x8d"), ESCAPE_VALUE("‌", "\xe2\x80\x8c"), }; /** * @internal * @var escape_values_e_common_sorted[] * same as escape_values_e_sorted with small subset of common escapes */ static const Escape_Value escape_values_e_common_sorted[] = { ESCAPE_VALUE("&", "\x26"), ESCAPE_VALUE("'", "\x27"), ESCAPE_VALUE(">", "\x3e"), ESCAPE_VALUE("<", "\x3c"), ESCAPE_VALUE(""", "\x22"), }; /** * @internal * @var escape_values_v_sorted[] * This array consists of Escape_Value structure sorted by escape value * And new added value must be placed sorted position, and reflected on escape_values_e_sorted */ static const Escape_Value escape_values_v_sorted[] = { ESCAPE_VALUE(" ", "\xc2\xa0"), ESCAPE_VALUE("¡", "\xc2\xa1"), ESCAPE_VALUE("¢", "\xc2\xa2"), ESCAPE_VALUE("£", "\xc2\xa3"), ESCAPE_VALUE("¤", "\xc2\xa4"), ESCAPE_VALUE("¥", "\xc2\xa5"), ESCAPE_VALUE("¦", "\xc2\xa6"), ESCAPE_VALUE("§", "\xc2\xa7"), ESCAPE_VALUE("¨", "\xc2\xa8"), ESCAPE_VALUE("©", "\xc2\xa9"), ESCAPE_VALUE("ª", "\xc2\xaa"), ESCAPE_VALUE("«", "\xc2\xab"), ESCAPE_VALUE("¬", "\xc2\xac"), ESCAPE_VALUE("­", "\xc2\xad"), ESCAPE_VALUE("®", "\xc2\xae"), ESCAPE_VALUE("¯", "\xc2\xaf"), ESCAPE_VALUE("°", "\xc2\xb0"), ESCAPE_VALUE("±", "\xc2\xb1"), ESCAPE_VALUE("²", "\xc2\xb2"), ESCAPE_VALUE("³", "\xc2\xb3"), ESCAPE_VALUE("´", "\xc2\xb4"), ESCAPE_VALUE("µ", "\xc2\xb5"), ESCAPE_VALUE("¶", "\xc2\xb6"), ESCAPE_VALUE("·", "\xc2\xb7"), ESCAPE_VALUE("¸", "\xc2\xb8"), ESCAPE_VALUE("¹", "\xc2\xb9"), ESCAPE_VALUE("º", "\xc2\xba"), ESCAPE_VALUE("»", "\xc2\xbb"), ESCAPE_VALUE("¼", "\xc2\xbc"), ESCAPE_VALUE("½", "\xc2\xbd"), ESCAPE_VALUE("¾", "\xc2\xbe"), ESCAPE_VALUE("¿", "\xc2\xbf"), ESCAPE_VALUE("À", "\xc3\x80"), ESCAPE_VALUE("Á", "\xc3\x81"), ESCAPE_VALUE("Â", "\xc3\x82"), ESCAPE_VALUE("Ã", "\xc3\x83"), ESCAPE_VALUE("Ä", "\xc3\x84"), ESCAPE_VALUE("Å", "\xc3\x85"), ESCAPE_VALUE("&Aelig;", "\xc3\x86"), ESCAPE_VALUE("Ç", "\xc3\x87"), ESCAPE_VALUE("È", "\xc3\x88"), ESCAPE_VALUE("É", "\xc3\x89"), ESCAPE_VALUE("Ê", "\xc3\x8a"), ESCAPE_VALUE("Ë", "\xc3\x8b"), ESCAPE_VALUE("Ì", "\xc3\x8c"), ESCAPE_VALUE("Í", "\xc3\x8d"), ESCAPE_VALUE("Î", "\xc3\x8e"), ESCAPE_VALUE("Ï", "\xc3\x8f"), ESCAPE_VALUE("&Eth;", "\xc3\x90"), ESCAPE_VALUE("Ñ", "\xc3\x91"), ESCAPE_VALUE("Ò", "\xc3\x92"), ESCAPE_VALUE("Ó", "\xc3\x93"), ESCAPE_VALUE("Ô", "\xc3\x94"), ESCAPE_VALUE("Õ", "\xc3\x95"), ESCAPE_VALUE("Ö", "\xc3\x96"), ESCAPE_VALUE("×", "\xc3\x97"), ESCAPE_VALUE("Ø", "\xc3\x98"), ESCAPE_VALUE("Ù", "\xc3\x99"), ESCAPE_VALUE("Ú", "\xc3\x9a"), ESCAPE_VALUE("Û", "\xc3\x9b"), ESCAPE_VALUE("Ý", "\xc3\x9d"), ESCAPE_VALUE("&Thorn;", "\xc3\x9e"), ESCAPE_VALUE("ß", "\xc3\x9f"), ESCAPE_VALUE("à", "\xc3\xa0"), ESCAPE_VALUE("á", "\xc3\xa1"), ESCAPE_VALUE("â", "\xc3\xa2"), ESCAPE_VALUE("ã", "\xc3\xa3"), ESCAPE_VALUE("ä", "\xc3\xa4"), ESCAPE_VALUE("å", "\xc3\xa5"), ESCAPE_VALUE("æ", "\xc3\xa6"), ESCAPE_VALUE("ç", "\xc3\xa7"), ESCAPE_VALUE("è", "\xc3\xa8"), ESCAPE_VALUE("é", "\xc3\xa9"), ESCAPE_VALUE("ê", "\xc3\xaa"), ESCAPE_VALUE("ë", "\xc3\xab"), ESCAPE_VALUE("ì", "\xc3\xac"), ESCAPE_VALUE("í", "\xc3\xad"), ESCAPE_VALUE("î", "\xc3\xae"), ESCAPE_VALUE("ï", "\xc3\xaf"), ESCAPE_VALUE("ð", "\xc3\xb0"), ESCAPE_VALUE("ñ", "\xc3\xb1"), ESCAPE_VALUE("ò", "\xc3\xb2"), ESCAPE_VALUE("ó", "\xc3\xb3"), ESCAPE_VALUE("ô", "\xc3\xb4"), ESCAPE_VALUE("õ", "\xc3\xb5"), ESCAPE_VALUE("ö", "\xc3\xb6"), ESCAPE_VALUE("÷", "\xc3\xb7"), ESCAPE_VALUE("ø", "\xc3\xb8"), ESCAPE_VALUE("ù", "\xc3\xb9"), ESCAPE_VALUE("ú", "\xc3\xba"), ESCAPE_VALUE("û", "\xc3\xbb"), ESCAPE_VALUE("ü", "\xc3\xbc"), ESCAPE_VALUE("ý", "\xc3\xbd"), ESCAPE_VALUE("þ", "\xc3\xbe"), ESCAPE_VALUE("ÿ", "\xc3\xbf"), ESCAPE_VALUE("α", "\xce\x91"), ESCAPE_VALUE("β", "\xce\x92"), ESCAPE_VALUE("γ", "\xce\x93"), ESCAPE_VALUE("δ", "\xce\x94"), ESCAPE_VALUE("ε", "\xce\x95"), ESCAPE_VALUE("ζ", "\xce\x96"), ESCAPE_VALUE("η", "\xce\x97"), ESCAPE_VALUE("θ", "\xce\x98"), ESCAPE_VALUE("ι", "\xce\x99"), ESCAPE_VALUE("κ", "\xce\x9a"), ESCAPE_VALUE("λ", "\xce\x9b"), ESCAPE_VALUE("μ", "\xce\x9c"), ESCAPE_VALUE("ν", "\xce\x9d"), ESCAPE_VALUE("ξ", "\xce\x9e"), ESCAPE_VALUE("ο", "\xce\x9f"), ESCAPE_VALUE("π", "\xce\xa0"), ESCAPE_VALUE("ρ", "\xce\xa1"), ESCAPE_VALUE("σ", "\xce\xa3"), ESCAPE_VALUE("τ", "\xce\xa4"), ESCAPE_VALUE("υ", "\xce\xa5"), ESCAPE_VALUE("φ", "\xce\xa6"), ESCAPE_VALUE("χ", "\xce\xa7"), ESCAPE_VALUE("ψ", "\xce\xa8"), ESCAPE_VALUE("ω", "\xce\xa9"), ESCAPE_VALUE("‌", "\xe2\x80\x8c"), ESCAPE_VALUE("‍", "\xe2\x80\x8d"), ESCAPE_VALUE("‎", "\xe2\x80\x8e"), ESCAPE_VALUE("‏", "\xe2\x80\x8f"), ESCAPE_VALUE("†", "\xe2\x80\xa0"), ESCAPE_VALUE("‡", "\xe2\x80\xa1"), ESCAPE_VALUE("•", "\xe2\x80\xa2"), ESCAPE_VALUE("…", "\xe2\x80\xa6"), ESCAPE_VALUE("€", "\xe2\x82\xac"), ESCAPE_VALUE("←", "\xe2\x86\x90"), ESCAPE_VALUE("↑", "\xe2\x86\x91"), ESCAPE_VALUE("→", "\xe2\x86\x92"), ESCAPE_VALUE("↓", "\xe2\x86\x93"), ESCAPE_VALUE("↔", "\xe2\x86\x94"), ESCAPE_VALUE("←", "\xe2\x87\x90"), ESCAPE_VALUE("→", "\xe2\x87\x92"), ESCAPE_VALUE("∀", "\xe2\x88\x80"), ESCAPE_VALUE("∃", "\xe2\x88\x83"), ESCAPE_VALUE("∇", "\xe2\x88\x87"), ESCAPE_VALUE("∏", "\xe2\x88\x8f"), ESCAPE_VALUE("∑", "\xe2\x88\x91"), ESCAPE_VALUE("∧", "\xe2\x88\xa7"), ESCAPE_VALUE("∨", "\xe2\x88\xa8"), ESCAPE_VALUE("∫", "\xe2\x88\xab"), ESCAPE_VALUE("≠", "\xe2\x89\xa0"), ESCAPE_VALUE("≡", "\xe2\x89\xa1"), ESCAPE_VALUE("⊕", "\xe2\x8a\x95"), ESCAPE_VALUE("⊥", "\xe2\x8a\xa5"), }; /** * @internal * @var escape_values_v_common_sorted[] * same as escape_values_v_sorted with small subset of common escapes */ static const Escape_Value escape_values_v_common_sorted[] = { ESCAPE_VALUE(""", "\x22"), ESCAPE_VALUE("&", "\x26"), ESCAPE_VALUE("'", "\x27"), ESCAPE_VALUE("<", "\x3c"), ESCAPE_VALUE(">", "\x3e"), }; /** * @internal * Checks if a char is a whitespace. * @param c the unicode codepoint. * @return @c EINA_TRUE if the unicode codepoint is a whitespace, @c EINA_FALSE * otherwise. */ static Eina_Bool _is_white(Eina_Unicode c) { /* * unicode list of whitespace chars * * 0009..000D .. * 0020 SPACE * 0085 * 00A0 NO-BREAK SPACE * 1680 OGHAM SPACE MARK * 180E MONGOLIAN VOWEL SEPARATOR * 2000..200A EN QUAD..HAIR SPACE * 2028 LINE SEPARATOR * 2029 PARAGRAPH SEPARATOR * 202F NARROW NO-BREAK SPACE * 205F MEDIUM MATHEMATICAL SPACE * 3000 IDEOGRAPHIC SPACE */ if ( (c == 0x20) || ((c >= 0x9) && (c <= 0xd)) || (c == 0x85) || (c == 0xa0) || (c == 0x1680) || (c == 0x180e) || ((c >= 0x2000) && (c <= 0x200a)) || (c == 0x2028) || (c == 0x2029) || (c == 0x202f) || (c == 0x205f) || (c == 0x3000) ) return EINA_TRUE; return EINA_FALSE; } /** * @internal * Prepends the text between s and p to the main cursor of the object. * * @param cur the cursor to prepend to. * @param[in] s start of the string * @param[in] p end of the string */ static void _prepend_text_run(Efl_Text_Cursor_Handle *cur, const char *s, const char *p) { if ((s) && (p > s)) { char *ts; ts = alloca(p - s + 1); strncpy(ts, s, p - s); ts[p - s] = 0; evas_textblock_cursor_text_prepend(cur, ts); } } /* The refcount for the formats. */ static int format_refcount = 0; /* Holders for the stringshares */ static const char *fontstr = NULL; static const char *font_fallbacksstr = NULL; static const char *font_sizestr = NULL; static const char *font_sourcestr = NULL; static const char *font_weightstr = NULL; static const char *font_stylestr = NULL; static const char *font_widthstr = NULL; static const char *langstr = NULL; static const char *colorstr = NULL; static const char *underline_colorstr = NULL; static const char *underline2_colorstr = NULL; static const char *secondary_underline_colorstr = NULL; static const char *underline_dash_colorstr = NULL; static const char *underline_dashed_colorstr = NULL; static const char *outline_colorstr = NULL; static const char *shadow_colorstr = NULL; static const char *glow_colorstr = NULL; static const char *glow2_colorstr = NULL; static const char *secondary_glow_colorstr = NULL; static const char *backing_colorstr = NULL; static const char *background_colorstr = NULL; static const char *strikethrough_colorstr = NULL; static const char *alignstr = NULL; static const char *valignstr = NULL; static const char *text_valignstr = NULL; static const char *wrapstr = NULL; static const char *left_marginstr = NULL; static const char *right_marginstr = NULL; static const char *underlinestr = NULL; static const char *underline_typestr = NULL; static const char *strikethroughstr = NULL; static const char *strikethrough_typestr = NULL; static const char *backingstr = NULL; static const char *background_typestr = NULL; static const char *stylestr = NULL; static const char *effect_typestr = NULL; static const char *shadow_directionstr = NULL; static const char *tabstopsstr = NULL; static const char *tab_stopsstr = NULL; static const char *linesizestr = NULL; static const char *line_sizestr = NULL; static const char *linerelsizestr = NULL; static const char *line_rel_sizestr = NULL; static const char *linegapstr = NULL; static const char *line_gapstr = NULL; static const char *linerelgapstr = NULL; static const char *line_rel_gapstr = NULL; static const char *itemstr = NULL; static const char *linefillstr = NULL; static const char *line_fillstr = NULL; static const char *ellipsisstr = NULL; static const char *passwordstr = NULL; static const char *replacement_charstr = NULL; static const char *underline_dash_widthstr = NULL; static const char *underline_dashed_widthstr = NULL; static const char *underline_dash_gapstr = NULL; static const char *underline_dashed_gapstr = NULL; static const char *underline_heightstr = NULL; static const char *gfx_filterstr = NULL; /** * @page evas_textblock_style_page Evas Textblock Style Options * * @brief This page describes how to style text in an Evas Text Block. */ /** * Internal * Split str using commas as separators. All characters from the beginning of the string up until * the first comma (excluded) are copied into part1. * All characters after the last comma (excluded) up until the end of str are copied into part2. * Any character in between part1 and part2 is ignored (as right now it's only valid to have 1 comma and 2 strings). * For example, if str="str1,str2,str3,str4", * part1 will contain "str1" and part2 will contain "str4". * part1 and part2 must be already allocated and contain enough space for any possible outcome * of the parsing. The safest bet is that they should be as big as str. */ void _style_string_split(const char *str, char* part1, char* part2) { char *temp = part1; for (const char *p = str; *p; p++) { if (*p == ',') { *temp = 0; temp = part2; continue; } *temp = *p; temp++; } *temp = 0; } #define FORMAT_SHADOW_SET(evas, efl) {fmt->style = evas; if (set_default) _FMT_INFO(effect) = efl;} void _format_shadow_set(Evas_Object_Textblock_Format *fmt, char *str, Eina_Bool set_default, Efl_Canvas_Textblock_Data *o) { if (!strcmp(str, "shadow")) FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_SHADOW, EFL_TEXT_STYLE_EFFECT_TYPE_SHADOW) else if (!strcmp(str, "outline")) FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_OUTLINE, EFL_TEXT_STYLE_EFFECT_TYPE_OUTLINE) else if (!strcmp(str, "soft_outline")) FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_SOFT_OUTLINE, EFL_TEXT_STYLE_EFFECT_TYPE_SOFT_OUTLINE) else if (!strcmp(str, "outline_shadow")) FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_OUTLINE_SHADOW, EFL_TEXT_STYLE_EFFECT_TYPE_OUTLINE_SHADOW) else if (!strcmp(str, "outline_soft_shadow")) FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_OUTLINE_SOFT_SHADOW, EFL_TEXT_STYLE_EFFECT_TYPE_OUTLINE_SOFT_SHADOW) else if (!strcmp(str, "glow")) FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_GLOW, EFL_TEXT_STYLE_EFFECT_TYPE_GLOW) else if (!strcmp(str, "far_shadow")) FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_FAR_SHADOW, EFL_TEXT_STYLE_EFFECT_TYPE_FAR_SHADOW) else if (!strcmp(str, "soft_shadow")) FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_SOFT_SHADOW, EFL_TEXT_STYLE_EFFECT_TYPE_SOFT_SHADOW) else if (!strcmp(str, "far_soft_shadow")) FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_FAR_SOFT_SHADOW, EFL_TEXT_STYLE_EFFECT_TYPE_FAR_SOFT_SHADOW) else /*off none plain */ FORMAT_SHADOW_SET(EVAS_TEXT_STYLE_PLAIN, EFL_TEXT_STYLE_EFFECT_TYPE_NONE) } #define FORMAT_SHADOW_DIRECTION_SET(direction) {EVAS_TEXT_STYLE_SHADOW_DIRECTION_SET(fmt->style, EVAS_TEXT_STYLE_SHADOW_DIRECTION_##direction); if (set_default) _FMT_INFO(shadow_direction) = EFL_TEXT_STYLE_SHADOW_DIRECTION_##direction;} void _format_shadow_direction_set(Evas_Object_Textblock_Format *fmt, char *str, Eina_Bool set_default, Efl_Canvas_Textblock_Data *o) { if (!strcmp(str, "bottom_right")) FORMAT_SHADOW_DIRECTION_SET(BOTTOM_RIGHT) else if (!strcmp(str, "bottom")) FORMAT_SHADOW_DIRECTION_SET(BOTTOM) else if (!strcmp(str, "bottom_left")) FORMAT_SHADOW_DIRECTION_SET(BOTTOM_LEFT) else if (!strcmp(str, "left")) FORMAT_SHADOW_DIRECTION_SET(LEFT) else if (!strcmp(str, "top_left")) FORMAT_SHADOW_DIRECTION_SET(TOP_LEFT) else if (!strcmp(str, "top")) FORMAT_SHADOW_DIRECTION_SET(TOP) else if (!strcmp(str, "top_right")) FORMAT_SHADOW_DIRECTION_SET(TOP_RIGHT) else if (!strcmp(str, "right")) FORMAT_SHADOW_DIRECTION_SET(RIGHT) else FORMAT_SHADOW_DIRECTION_SET(BOTTOM_RIGHT) } /** * @internal * Init the format strings. */ static void _format_command_init(void) { if (format_refcount == 0) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @section evas_textblock_style_index Index * * The following styling commands are accepted: * @li @ref evas_textblock_style_font * @li @ref evas_textblock_style_font_fallback * @li @ref evas_textblock_style_font_size * @li @ref evas_textblock_style_font_source * @li @ref evas_textblock_style_font_weight * @li @ref evas_textblock_style_font_style * @li @ref evas_textblock_style_font_width * @li @ref evas_textblock_style_lang * @li @ref evas_textblock_style_color * @li @ref evas_textblock_style_underline_color * @li @ref evas_textblock_style_underline2_color * @li @ref evas_textblock_style_underline_dash_color * @li @ref evas_textblock_style_outline_color * @li @ref evas_textblock_style_shadow_color * @li @ref evas_textblock_style_glow_color * @li @ref evas_textblock_style_glow2_color * @li @ref evas_textblock_style_backing_color * @li @ref evas_textblock_style_strikethrough_color * @li @ref evas_textblock_style_align * @li @ref evas_textblock_style_valign * @li @ref evas_textblock_style_wrap * @li @ref evas_textblock_style_left_margin * @li @ref evas_textblock_style_right_margin * @li @ref evas_textblock_style_underline * @li @ref evas_textblock_style_strikethrough * @li @ref evas_textblock_style_backing * @li @ref evas_textblock_style_style * @li @ref evas_textblock_style_tabstops * @li @ref evas_textblock_style_linesize * @li @ref evas_textblock_style_linerelsize * @li @ref evas_textblock_style_linegap * @li @ref evas_textblock_style_linerelgap * @li @ref evas_textblock_style_item * @li @ref evas_textblock_style_linefill * @li @ref evas_textblock_style_ellipsis * @li @ref evas_textblock_style_password * @li @ref evas_textblock_style_underline_dash_width * @li @ref evas_textblock_style_underline_dash_gap * @li @ref evas_textblock_style_underline_height * * @section evas_textblock_style_contents Contents */ fontstr = eina_stringshare_add("font"); font_fallbacksstr = eina_stringshare_add("font_fallbacks"); font_sizestr = eina_stringshare_add("font_size"); font_sourcestr = eina_stringshare_add("font_source"); font_weightstr = eina_stringshare_add("font_weight"); font_stylestr = eina_stringshare_add("font_style"); font_widthstr = eina_stringshare_add("font_width"); langstr = eina_stringshare_add("lang"); colorstr = eina_stringshare_add("color"); underline_colorstr = eina_stringshare_add("underline_color"); underline2_colorstr = eina_stringshare_add("underline2_color"); secondary_underline_colorstr = eina_stringshare_add("secondary_underline_color"); underline_dash_colorstr = eina_stringshare_add("underline_dash_color"); underline_dashed_colorstr = eina_stringshare_add("underline_dashed_color"); outline_colorstr = eina_stringshare_add("outline_color"); shadow_colorstr = eina_stringshare_add("shadow_color"); glow_colorstr = eina_stringshare_add("glow_color"); glow2_colorstr = eina_stringshare_add("glow2_color"); secondary_glow_colorstr = eina_stringshare_add("secondary_glow_color"); backing_colorstr = eina_stringshare_add("backing_color"); background_colorstr = eina_stringshare_add("background_color"); strikethrough_colorstr = eina_stringshare_add("strikethrough_color"); alignstr = eina_stringshare_add("align"); valignstr = eina_stringshare_add("valign"); text_valignstr = eina_stringshare_add("text_valign"); wrapstr = eina_stringshare_add("wrap"); left_marginstr = eina_stringshare_add("left_margin"); right_marginstr = eina_stringshare_add("right_margin"); underlinestr = eina_stringshare_add("underline"); underline_typestr = eina_stringshare_add("underline_type"); strikethroughstr = eina_stringshare_add("strikethrough"); strikethrough_typestr = eina_stringshare_add("strikethrough_type"); backingstr = eina_stringshare_add("backing"); background_typestr = eina_stringshare_add("background_type"); stylestr = eina_stringshare_add("style"); effect_typestr = eina_stringshare_add("effect_type"); shadow_directionstr = eina_stringshare_add("shadow_direction"); tabstopsstr = eina_stringshare_add("tabstops"); tab_stopsstr = eina_stringshare_add("tab_stops"); linesizestr = eina_stringshare_add("linesize"); line_sizestr = eina_stringshare_add("line_size"); linerelsizestr = eina_stringshare_add("linerelsize"); line_rel_sizestr = eina_stringshare_add("line_rel_size"); linegapstr = eina_stringshare_add("linegap"); line_gapstr = eina_stringshare_add("line_gap"); linerelgapstr = eina_stringshare_add("linerelgap"); line_rel_gapstr = eina_stringshare_add("line_rel_gap"); itemstr = eina_stringshare_add("item"); linefillstr = eina_stringshare_add("linefill"); line_fillstr = eina_stringshare_add("line_fill"); ellipsisstr = eina_stringshare_add("ellipsis"); passwordstr = eina_stringshare_add("password"); replacement_charstr = eina_stringshare_add("replacement_char"); underline_dash_widthstr = eina_stringshare_add("underline_dash_width"); underline_dashed_widthstr = eina_stringshare_add("underline_dashed_width"); underline_dash_gapstr = eina_stringshare_add("underline_dash_gap"); underline_dashed_gapstr = eina_stringshare_add("underline_dashed_gap"); underline_heightstr = eina_stringshare_add("underline_height"); gfx_filterstr = eina_stringshare_add("gfx_filter"); // FIXME: bg, fg filters } format_refcount++; } /** * @internal * Shutdown the format strings. */ static void _format_command_shutdown(void) { if (--format_refcount > 0) return; eina_stringshare_del(fontstr); eina_stringshare_del(font_fallbacksstr); eina_stringshare_del(font_sizestr); eina_stringshare_del(font_sourcestr); eina_stringshare_del(font_weightstr); eina_stringshare_del(font_stylestr); eina_stringshare_del(font_widthstr); eina_stringshare_del(langstr); eina_stringshare_del(colorstr); eina_stringshare_del(underline_colorstr); eina_stringshare_del(underline2_colorstr); eina_stringshare_del(secondary_underline_colorstr); eina_stringshare_del(underline_dash_colorstr); eina_stringshare_del(underline_dashed_colorstr); eina_stringshare_del(outline_colorstr); eina_stringshare_del(shadow_colorstr); eina_stringshare_del(glow_colorstr); eina_stringshare_del(glow2_colorstr); eina_stringshare_del(secondary_glow_colorstr); eina_stringshare_del(backing_colorstr); eina_stringshare_del(background_colorstr); eina_stringshare_del(strikethrough_colorstr); eina_stringshare_del(alignstr); eina_stringshare_del(valignstr); eina_stringshare_del(text_valignstr); eina_stringshare_del(wrapstr); eina_stringshare_del(left_marginstr); eina_stringshare_del(right_marginstr); eina_stringshare_del(underlinestr); eina_stringshare_del(underline_typestr); eina_stringshare_del(strikethroughstr); eina_stringshare_del(strikethrough_typestr); eina_stringshare_del(backingstr); eina_stringshare_del(background_typestr); eina_stringshare_del(stylestr); eina_stringshare_del(effect_typestr); eina_stringshare_del(shadow_directionstr); eina_stringshare_del(tabstopsstr); eina_stringshare_del(tab_stopsstr); eina_stringshare_del(linesizestr); eina_stringshare_del(line_sizestr); eina_stringshare_del(linerelsizestr); eina_stringshare_del(line_rel_sizestr); eina_stringshare_del(linegapstr); eina_stringshare_del(line_gapstr); eina_stringshare_del(linerelgapstr); eina_stringshare_del(line_rel_gapstr); eina_stringshare_del(itemstr); eina_stringshare_del(linefillstr); eina_stringshare_del(line_fillstr); eina_stringshare_del(ellipsisstr); eina_stringshare_del(passwordstr); eina_stringshare_del(replacement_charstr); eina_stringshare_del(underline_dash_widthstr); eina_stringshare_del(underline_dashed_widthstr); eina_stringshare_del(underline_dash_gapstr); eina_stringshare_del(underline_dashed_gapstr); eina_stringshare_del(underline_heightstr); eina_stringshare_del(gfx_filterstr); } /** * @internal * Copies str to dst while removing the \\ char, i.e unescape the escape sequences. * * @param[out] dst the destination string - Should not be NULL. * @param[in] src the source string - Should not be NULL. */ static int _format_clean_param(char *s) { char *ss; char *ds; int len = 0; ds = s; for (ss = s; *ss; ss++, ds++, len++) { if ((*ss == '\\') && *(ss + 1)) ss++; if (ds != ss) *ds = *ss; } *ds = 0; return len; } static void _format_command_legacy_only(Evas_Object_Textblock_Format *fmt, const char *cmd, char *param, int len) { if (cmd == backing_colorstr) /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_backing_color Backing Color * * Sets a background color for text. The following formats are * accepted: * @li "#RRGGBB" * @li "#RRGGBBAA" * @li "#RGB" * @li "#RGBA" * @li "rgb(r,g,b)" * @li "rgba(r,g,b,a)" * @li "color_name" like "red" * @code * backing_color= * @endcode */ evas_common_format_color_parse(param, len, &(fmt->color.backing.r), &(fmt->color.backing.g), &(fmt->color.backing.b), &(fmt->color.backing.a)); else if (cmd == backingstr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_backing Backing * * Sets if the text will have backing. The value must be one of * the following: * @li "off" - No backing * @li "on" - Backing * @code * backing=on/off * @endcode */ if (len == 3 && !strcmp(param, "off")) fmt->backing = 0; else if (len == 2 && !strcmp(param, "on")) fmt->backing = 1; } else if (cmd == underline2_colorstr) /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_underline2_color Second Underline Color * * Sets the color of the second line of underline(when using underline * mode "double"). The following formats are accepted: * @li "#RRGGBB" * @li "#RRGGBBAA" * @li "#RGB" * @li "#RGBA" * @li "rgb(r,g,b)" * @li "rgba(r,g,b,a)" * @li "color_name" like "red" * @code * underline2_color= * @endcode */ evas_common_format_color_parse(param, len, &(fmt->color.underline2.r), &(fmt->color.underline2.g), &(fmt->color.underline2.b), &(fmt->color.underline2.a)); else if (cmd == glow2_colorstr) /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_glow2_color Second Glow Color * * Sets the second color of the glow of text. The following formats are * accepted: * @li "#RRGGBB" * @li "#RRGGBBAA" * @li "#RGB" * @li "#RGBA" * @li "rgb(r,g,b)" * @li "rgba(r,g,b,a)" * @li "color_name" like "red" * @code * glow2_color= * @endcode */ evas_common_format_color_parse(param, len, &(fmt->color.glow2.r), &(fmt->color.glow2.g), &(fmt->color.glow2.b), &(fmt->color.glow2.a)); else if (cmd == tabstopsstr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_tabstops Tabstops * * Sets the size of the tab character. The value must be a number * greater than one. * @code * tabstops= * @endcode */ fmt->tabstops = atoi(param); if (fmt->tabstops < 1) fmt->tabstops = 1; } else if (cmd == linesizestr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_linesize Line size * * Sets the size of line of text. The value should be a number. * @warning Setting this value sets linerelsize to 0%! * @code * linesize= * @endcode */ fmt->linesize = atoi(param); fmt->linerelsize = 0.0; } else if (cmd == linerelsizestr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_linerelsize Relative line size * * Sets the relative size of line of text. The value must be a * percentage. * @warning Setting this value sets linesize to 0! * @code * linerelsize=% * @endcode */ char *endptr = NULL; double val = strtod(param, &endptr); if (endptr) { while (*endptr && _is_white(*endptr)) endptr++; if (*endptr == '%') { fmt->linerelsize = val / 100.0; fmt->linesize = 0; if (fmt->linerelsize < 0.0) fmt->linerelsize = 0.0; } } } else if (cmd == linegapstr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_linegap Line gap * * Sets the size of the line gap in text. The value should be a * number. * @warning Setting this value sets linerelgap to 0%! * @code * linegap= * @endcode */ fmt->linegap = atoi(param); fmt->linerelgap = 0.0; } else if (cmd == linerelgapstr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_linerelgap Relative line gap * * Sets the relative size of the line gap in text. The value must be * a percentage. * @warning Setting this value sets linegap to 0! * @code * linerelgap=% * @endcode */ char *endptr = NULL; double val = strtod(param, &endptr); if (endptr) { while (*endptr && _is_white(*endptr)) endptr++; if (*endptr == '%') { fmt->linerelgap = val / 100.0; fmt->linegap = 0; if (fmt->linerelgap < 0.0) fmt->linerelgap = 0.0; } } } else if (cmd == linefillstr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_linefill Line fill * * Sets the size of the line fill in text. The value must be a * percentage. * @code * linefill=% * @endcode */ char *endptr = NULL; double val = strtod(param, &endptr); if (endptr) { while (*endptr && _is_white(*endptr)) endptr++; if (*endptr == '%') { fmt->linefill = val / 100.0; if (fmt->linefill < 0.0) fmt->linefill = 0.0; } } } else if (cmd == underline_dash_colorstr) /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_underline_dash_color Underline Dash Color * * Sets the color of dashed underline. The following formats are accepted: * @li "#RRGGBB" * @li "#RRGGBBAA" * @li "#RGB" * @li "#RGBA" * @li "rgb(r,g,b)" * @li "rgba(r,g,b,a)" * @li "color_name" like "red" * @code * underline_dash_color= * @endcode */ evas_common_format_color_parse(param, len, &(fmt->color.underline_dash.r), &(fmt->color.underline_dash.g), &(fmt->color.underline_dash.b), &(fmt->color.underline_dash.a)); else if (cmd == underlinestr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_underline Underline * * Sets if and how a text will be underlined. The value must be one of * the following: * @li "off" - No underlining * @li "single" - A single line under the text * @li "on" - Alias for "single" * @li "double" - Two lines under the text * @li "dashed" - A dashed line under the text * @code * underline=off/single/on/double/dashed * @endcode */ static const struct { const char *param; int len; Eina_Bool underline; Eina_Bool underline2; Eina_Bool underline_dash; } underlines_named[] = { { "off", 3, 0, 0, 0 }, { "on", 2, 1, 0, 0 }, { "single", 6, 1, 0, 0 }, { "double", 6, 1, 1, 0 }, { "dashed", 6, 0, 0, 1 }, { NULL, 0, 0, 0, 0 } }; unsigned int i; fmt->underline = fmt->underline2 = fmt->underline_dash = 0; for (i = 0; underlines_named[i].param; ++i) if (underlines_named[i].len == len && !strcmp(underlines_named[i].param, param)) { fmt->underline = underlines_named[i].underline; fmt->underline2 = underlines_named[i].underline2; fmt->underline_dash = underlines_named[i].underline_dash; break; } } else if (cmd == strikethroughstr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_strikethrough Strikethrough * * Sets if the text will be striked through. The value must be one of * the following: * @li "off" - No strikethrough * @li "on" - Strikethrough * @code * strikethrough=on/off * @endcode */ if (len == 3 && !strcmp(param, "off")) fmt->strikethrough = 0; else if (len == 2 && !strcmp(param, "on")) fmt->strikethrough = 1; } else if (cmd == stylestr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_style Style * * Sets the style of the text. The value must be a string composed of * two comma separated parts. The first part of the value sets the * appearance of the text, the second the position. * * The first part may be any of the following values: * @li "plain" * @li "off" - Alias for "plain" * @li "none" - Alias for "plain" * @li "shadow" * @li "outline" * @li "soft_outline" * @li "outline_shadow" * @li "outline_soft_shadow" * @li "glow" * @li "far_shadow" * @li "soft_shadow" * @li "far_soft_shadow" * The second part may be any of the following values: * @li "bottom_right" * @li "bottom" * @li "bottom_left" * @li "left" * @li "top_left" * @li "top" * @li "top_right" * @li "right" * @code * style=, * @endcode */ char *part1, *part2; part1 = alloca(len + 1); *part1 = 0; part2 = alloca(len + 1); *part2 = 0; _style_string_split(param, part1, part2); _format_shadow_set(fmt, part1, EINA_FALSE, NULL); if (*part2) _format_shadow_direction_set(fmt, part2, EINA_FALSE, NULL); } else if (cmd == underline_dash_widthstr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_underline_dash_width Underline dash width * * Sets the width of the underline dash. The value should be a number. * @code * underline_dash_width= * @endcode */ fmt->underline_dash_width = atoi(param); if (fmt->underline_dash_width <= 0) fmt->underline_dash_width = 1; } else if (cmd == underline_dash_gapstr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_underline_dash_gap Underline dash gap * * Sets the gap of the underline dash. The value should be a number. * @code * underline_dash_gap= * @endcode */ fmt->underline_dash_gap = atoi(param); if (fmt->underline_dash_gap <= 0) fmt->underline_dash_gap = 1; } } static void _format_command_unified_only( Efl_Canvas_Textblock_Data *o, Evas_Object_Textblock_Format *fmt, const char *cmd, char *param, int len) { if (cmd == background_colorstr) evas_common_format_color_parse(param, len, &(fmt->color.backing.r), &(fmt->color.backing.g), &(fmt->color.backing.b), &(fmt->color.backing.a)); else if (cmd == background_typestr) { if (len == 4 && !strcmp(param, "none")) fmt->backing = 0; else if (len == 5 && !strcmp(param, "solid")) fmt->backing = 1; } else if (cmd == secondary_underline_colorstr) evas_common_format_color_parse(param, len, &(fmt->color.underline2.r), &(fmt->color.underline2.g), &(fmt->color.underline2.b), &(fmt->color.underline2.a)); else if (cmd == secondary_glow_colorstr) evas_common_format_color_parse(param, len, &(fmt->color.glow2.r), &(fmt->color.glow2.g), &(fmt->color.glow2.b), &(fmt->color.glow2.a)); else if (cmd == tab_stopsstr) { fmt->tabstops = atoi(param); if (fmt->tabstops < 1) fmt->tabstops = 1; } else if (cmd == line_sizestr) { fmt->linesize = atoi(param); fmt->linerelsize = 0.0; } else if (cmd == line_rel_sizestr) { char *endptr = NULL; double val = strtod(param, &endptr); if (endptr) { while (*endptr && _is_white(*endptr)) endptr++; if (*endptr == '%') { fmt->linerelsize = val / 100.0; fmt->linesize = 0; if (fmt->linerelsize < 0.0) fmt->linerelsize = 0.0; } } } else if (cmd == line_gapstr) { fmt->linegap = atoi(param); fmt->linerelgap = 0.0; } else if (cmd == line_rel_gapstr) { char *endptr = NULL; double val = strtod(param, &endptr); if (endptr) { while (*endptr && _is_white(*endptr)) endptr++; if (*endptr == '%') { fmt->linerelgap = val / 100.0; fmt->linegap = 0; if (fmt->linerelgap < 0.0) fmt->linerelgap = 0.0; } } } else if (cmd == line_fillstr) { char *endptr = NULL; double val = strtod(param, &endptr); if (endptr) { while (*endptr && _is_white(*endptr)) endptr++; if (*endptr == '%') { fmt->linefill = val / 100.0; if (fmt->linefill < 0.0) fmt->linefill = 0.0; } } } else if (cmd == underline_dashed_colorstr) evas_common_format_color_parse(param, len, &(fmt->color.underline_dash.r), &(fmt->color.underline_dash.g), &(fmt->color.underline_dash.b), &(fmt->color.underline_dash.a)); else if (cmd == underline_typestr) { typedef struct { const char *param; int len; Eina_Bool underline; Eina_Bool underline2; Eina_Bool underline_dash; } Underline_Info; fmt->underline = fmt->underline2 = fmt->underline_dash = 0; unsigned int i; #define SET_UNDERLINE_VALUES() \ for (i = 0; underlines_named[i].param; ++i) { \ if (underlines_named[i].len == len && \ !strcmp(underlines_named[i].param, param)) \ { \ fmt->underline = underlines_named[i].underline; \ fmt->underline2 = underlines_named[i].underline2; \ fmt->underline_dash = underlines_named[i].underline_dash; \ break;\ } \ } static Underline_Info underlines_named[] = { { "none", 4, 0, 0, 0 }, { "single", 6, 1, 0, 0 }, { "double", 6, 1, 1, 0 }, { "dashed", 6, 0, 0, 1 }, { NULL, 0, 0, 0, 0 } }; SET_UNDERLINE_VALUES(); } else if (cmd == strikethrough_typestr) { if (len == 4 && !strcmp(param, "none")) fmt->strikethrough = 0; else if (len == 6 && !strcmp(param, "single")) fmt->strikethrough = 1; } else if (cmd == effect_typestr) { _format_shadow_set(fmt, param, EINA_TRUE, o); } else if (cmd == shadow_directionstr) { _format_shadow_direction_set(fmt, param, EINA_TRUE, o); } else if (cmd == underline_dashed_widthstr) { fmt->underline_dash_width = atoi(param); if (fmt->underline_dash_width <= 0) fmt->underline_dash_width = 1; } else if (cmd == underline_dashed_gapstr) { fmt->underline_dash_gap = atoi(param); if (fmt->underline_dash_gap <= 0) fmt->underline_dash_gap = 1; } } /** * @internal * Parses the cmd and parameter and adds the parsed format to fmt. * * @param obj the evas object - should not be NULL. * @param fmt The format to populate - should not be NULL. * @param[in] cmd the command to process, should be stringshared. * @param[in] param the parameter of the command. may modify the string. */ static void _format_command(Evas_Object *eo_obj, Evas_Object_Textblock_Format *fmt, const char *cmd, char *param) { int len; Efl_Canvas_Textblock_Data *o = efl_data_scope_get(eo_obj, MY_CLASS); Eina_Bool is_legacy = o->is_legacy; len = _format_clean_param(param); /* If we are changing the font, create the fdesc. */ if ((cmd == font_weightstr) || (cmd == font_widthstr) || (cmd == font_stylestr) || (cmd == langstr) || (cmd == fontstr) || (cmd == font_fallbacksstr)) { if (!fmt->font.fdesc) { fmt->font.fdesc = evas_font_desc_new(); /* Set default language according to locale. */ eina_stringshare_replace(&(fmt->font.fdesc->lang), evas_font_lang_normalize("auto")); } else if (!fmt->font.fdesc->is_new) { Evas_Font_Description *old = fmt->font.fdesc; fmt->font.fdesc = evas_font_desc_dup(fmt->font.fdesc); if (old) evas_font_desc_unref(old); } } if (cmd == fontstr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_font Font * * This sets the name of the font to be used. * @code * font= * @endcode */ evas_font_name_parse(fmt->font.fdesc, param); } else if (cmd == font_fallbacksstr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_font_fallback Font fallback * * This sets the name of the fallback font to be used. This font will * be used if the primary font is not available. * @code * font_fallbacks= * @endcode */ eina_stringshare_replace(&(fmt->font.fdesc->fallbacks), param); } else if (cmd == font_sizestr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_font_size Font size * * This sets the the size of font in points to be used. * @code * font_size= * @endcode */ int v; v = atoi(param); if (v != fmt->font.size) { fmt->font.size = v; } } else if (cmd == font_sourcestr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_font_source Font source * * Specify an object from which to search for the font. * @code * font_source= * @endcode */ if ((!fmt->font.source) || ((fmt->font.source) && (strcmp(fmt->font.source, param)))) { eina_stringshare_replace(&(fmt->font.source), param); } } else if (cmd == font_weightstr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_font_weight Font weight * * Sets the weight of the font. The value must be one of: * @li "normal" * @li "thin" * @li "ultralight" * @li "extralight" * @li "light" * @li "book" * @li "medium" * @li "semibold" * @li "bold" * @li "ultrabold" * @li "extrabold" * @li "black" * @li "extrablack" * @code * font_weight= * @endcode */ fmt->font.fdesc->weight = evas_font_style_find(param, param + len, EVAS_FONT_STYLE_WEIGHT); } else if (cmd == font_stylestr) { /** * @page evas_textblock_style_page Evas Textblock Style Options * * @subsection evas_textblock_style_font_style Font style * * Sets the style of the font. The value must be one of: * @li "normal" * @li "oblique" * @li "italic" * @code * font_style=