/** * @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 */ #include "evas_common_private.h" #include "evas_private.h" #include #include "Eo.h" EAPI Eo_Op EVAS_OBJ_TEXTBLOCK_BASE_ID = EO_NOOP; #define MY_CLASS EVAS_OBJ_TEXTBLOCK_CLASS #define MY_CLASS_NAME "Evas_Object_Textblock" #include "linebreak.h" #include "wordbreak.h" /* save typing */ #define ENFN obj->layer->evas->engine.func #define ENDT obj->layer->evas->engine.data.output /* 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 _REPLACEMENT_CHAR_UTF8 "\xEF\xBF\xBC" #define _PARAGRAPH_SEPARATOR_UTF8 "\xE2\x80\xA9" #define _NEWLINE_UTF8 "\n" #define _TAB_UTF8 "\t" #define EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(ch) \ (((ch) == _REPLACEMENT_CHAR) || \ ((ch) == _NEWLINE) || \ ((ch) == _TAB) || \ ((ch) == _PARAGRAPH_SEPARATOR)) #ifdef CRITICAL #undef CRITICAL #endif #define CRITICAL(...) 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) /* private struct for textblock object internal data */ /** * @internal * @typedef Evas_Object_Textblock * The actual textblock object. */ typedef struct _Evas_Object_Textblock Evas_Object_Textblock; /** * @internal * @typedef Evas_Object_Style_Tag * The structure used for finding style tags. */ typedef struct _Evas_Object_Style_Tag Evas_Object_Style_Tag; /** * @internal * @typedef Evas_Object_Style_Tag * The structure used for finding style tags. */ typedef struct _Evas_Object_Style_Tag_Base Evas_Object_Style_Tag_Base; /** * @internal * @typedef Evas_Object_Textblock_Node_Text * A text node. */ typedef struct _Evas_Object_Textblock_Node_Text Evas_Object_Textblock_Node_Text; /* * Defined in Evas.h typedef struct _Evas_Object_Textblock_Node_Format Evas_Object_Textblock_Node_Format; */ /** * @internal * @typedef Evas_Object_Textblock_Paragraph * A layouting paragraph. */ typedef struct _Evas_Object_Textblock_Paragraph Evas_Object_Textblock_Paragraph; /** * @internal * @typedef Evas_Object_Textblock_Line * A layouting line. */ typedef struct _Evas_Object_Textblock_Line Evas_Object_Textblock_Line; /** * @internal * @typedef Evas_Object_Textblock_Item * A layouting item. */ typedef struct _Evas_Object_Textblock_Item Evas_Object_Textblock_Item; /** * @internal * @typedef Evas_Object_Textblock_Item * A layouting text item. */ typedef struct _Evas_Object_Textblock_Text_Item Evas_Object_Textblock_Text_Item; /** * @internal * @typedef Evas_Object_Textblock_Format_Item * A layouting format item. */ typedef struct _Evas_Object_Textblock_Format_Item Evas_Object_Textblock_Format_Item; /** * @internal * @typedef Evas_Object_Textblock_Format * A textblock format. */ typedef struct _Evas_Object_Textblock_Format Evas_Object_Textblock_Format; /** * @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_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]))) /*FIXME: document the structs and struct items. */ struct _Evas_Object_Style_Tag_Base { char *tag; char *replace; size_t tag_len; size_t replace_len; }; struct _Evas_Object_Style_Tag { EINA_INLIST; Evas_Object_Style_Tag_Base tag; }; struct _Evas_Object_Textblock_Node_Text { EINA_INLIST; Eina_UStrbuf *unicode; char *utf8; Evas_Object_Textblock_Node_Format *format_node; Evas_Object_Textblock_Paragraph *par; Eina_Bool dirty : 1; Eina_Bool is_new : 1; }; struct _Evas_Object_Textblock_Node_Format { EINA_INLIST; const char *format; const char *orig_format; Evas_Object_Textblock_Node_Text *text_node; size_t offset; unsigned char anchor : 2; Eina_Bool opener : 1; Eina_Bool own_closer : 1; Eina_Bool visible : 1; Eina_Bool format_change : 1; Eina_Bool is_new : 1; }; /* The default tags to use */ static const Evas_Object_Style_Tag_Base default_tags[] = { { "b", "+ font_weight=Bold", 1, 18 }, { "i", "+ font_style=Italic", 1, 19 }}; #define ANCHOR_NONE 0 #define ANCHOR_A 1 #define ANCHOR_ITEM 2 /** * @internal * @def _NODE_TEXT(x) * A convinience macro for casting to a text node. */ #define _NODE_TEXT(x) ((Evas_Object_Textblock_Node_Text *) (x)) /** * @internal * @def _NODE_FORMAT(x) * A convinience macro for casting to a format node. */ #define _NODE_FORMAT(x) ((Evas_Object_Textblock_Node_Format *) (x)) /** * @internal * @def _ITEM(x) * A convinience macro for casting to a generic item. */ #define _ITEM(x) ((Evas_Object_Textblock_Item *) (x)) /** * @internal * @def _ITEM_TEXT(x) * A convinience macro for casting to a text item. */ #define _ITEM_TEXT(x) ((Evas_Object_Textblock_Text_Item *) (x)) /** * @internal * @def _ITEM_FORMAT(x) * A convinience 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; Evas_Object_Textblock_Node_Text *text_node; Eina_List *logical_items; Evas_BiDi_Paragraph_Props *bidi_props; /* Only valid during layout */ Evas_BiDi_Direction direction; Evas_Coord y, w, h; int line_no; Eina_Bool is_bidi : 1; Eina_Bool visible : 1; Eina_Bool rendered : 1; }; struct _Evas_Object_Textblock_Line { EINA_INLIST; Evas_Object_Textblock_Item *items; Evas_Object_Textblock_Paragraph *par; Evas_Coord x, y, w, h; int baseline; int line_no; }; typedef enum _Evas_Textblock_Item_Type { EVAS_TEXTBLOCK_ITEM_TEXT, EVAS_TEXTBLOCK_ITEM_FORMAT, } Evas_Textblock_Item_Type; struct _Evas_Object_Textblock_Item { EINA_INLIST; Evas_Textblock_Item_Type type; Evas_Object_Textblock_Node_Text *text_node; Evas_Object_Textblock_Format *format; size_t text_pos; #ifdef BIDI_SUPPORT size_t visual_pos; #endif Evas_Coord adv, x, w, h; 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; Evas_Text_Props text_props; Evas_Coord inset; Evas_Coord x_adjustment; /* Used to indicate by how much we adjusted sizes */ }; struct _Evas_Object_Textblock_Format_Item { Evas_Object_Textblock_Item parent; Evas_BiDi_Direction bidi_dir; const char *item; int y; unsigned char vsize : 2; unsigned char size : 2; Eina_Bool formatme : 1; }; struct _Evas_Object_Textblock_Format { Evas_Object_Textblock_Node_Format *fnode; double halign; double valign; struct { Evas_Font_Description *fdesc; const char *source; Evas_Font_Set *font; Evas_Font_Size size; } 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; int ref; int tabstops; int linesize; int linegap; int underline_dash_width; int underline_dash_gap; double linerelsize; double linerelgap; double linefill; double ellipsis; unsigned char style; Eina_Bool wrap_word : 1; Eina_Bool wrap_char : 1; Eina_Bool wrap_mixed : 1; Eina_Bool underline : 1; Eina_Bool underline2 : 1; Eina_Bool underline_dash : 1; Eina_Bool strikethrough : 1; Eina_Bool backing : 1; Eina_Bool password : 1; Eina_Bool halign_auto : 1; }; struct _Evas_Textblock_Style { const char *style_text; char *default_tag; Evas_Object_Style_Tag *tags; Eina_List *objects; Eina_Bool delete_me : 1; }; struct _Evas_Textblock_Cursor { Evas_Object *obj; size_t pos; Evas_Object_Textblock_Node_Text *node; }; /* Size of the index array */ #define TEXTBLOCK_PAR_INDEX_SIZE 10 struct _Evas_Object_Textblock { DATA32 magic; Evas_Textblock_Style *style; Evas_Textblock_Style *style_user; Evas_Textblock_Cursor *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; int last_w, last_h; struct { int l, r, t, b; } style_pad; double valign; char *markup_text; void *engine_data; const char *repch; const char *bidi_delimiters; struct { int w, h; Eina_Bool valid : 1; } formatted, native; Eina_Bool redraw : 1; Eina_Bool changed : 1; Eina_Bool content_changed : 1; Eina_Bool format_changed : 1; Eina_Bool have_ellipsis : 1; Eina_Bool legacy_newline : 1; }; /* 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 *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 unsigned int evas_object_textblock_id_get(Evas_Object *eo_obj); static unsigned int evas_object_textblock_visual_id_get(Evas_Object *eo_obj); 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 evas_object_textblock_scale_update(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, void *type_private_data); 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_id_get, evas_object_textblock_visual_id_get, evas_object_textblock_engine_data_get, /* these are optional. NULL = nothing */ NULL, NULL, NULL, NULL, evas_object_textblock_is_opaque, evas_object_textblock_was_opaque, NULL, NULL, evas_object_textblock_coords_recalc, evas_object_textblock_scale_update, NULL, NULL, NULL }; /* the actual api call to add a textblock */ #define TB_HEAD() \ MAGIC_CHECK(eo_obj, Evas_Object, MAGIC_OBJ); \ return; \ MAGIC_CHECK_END(); \ Evas_Object_Textblock *o = eo_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 Evas_Textblock_Cursor *cur); static void _evas_textblock_node_text_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Text *n); static Evas_Object_Textblock_Node_Format *_evas_textblock_cursor_node_format_before_or_at_pos_get(const Evas_Textblock_Cursor *cur); static size_t _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_Node_Format *fmt); static void _evas_textblock_node_format_remove(Evas_Object_Textblock *o, Evas_Object_Textblock_Node_Format *n, int visual_adjustment); static void _evas_textblock_node_format_free(Evas_Object_Textblock *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(Evas_Object_Textblock *o, Evas_Object *eo_obj); static void _evas_textblock_invalidate_all(Evas_Object_Textblock *o); static void _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Text *n, size_t start, int offset); static void _evas_textblock_cursors_set_node(Evas_Object_Textblock *o, const Evas_Object_Textblock_Node_Text *n, Evas_Object_Textblock_Node_Text *new_node); /* 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) free(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)); free(tag->tag.tag); free(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 replcaement 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, size_t *replace_len) { Evas_Object_Style_Tag *tag; /* Try the style tags */ EINA_INLIST_FOREACH(ts->tags, tag) { if (tag->tag.tag_len != tag_len) continue; if (!strncmp(tag->tag.tag, s, tag_len)) { *replace_len = tag->tag.replace_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)) { *replace_len = btag->replace_len; return btag->replace; } } } *replace_len = 0; 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) { Evas_Object_Textblock *o = eo_data_scope_get(eo_obj, MY_CLASS); 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(const Evas_Object *eo_obj, Evas_Object_Textblock_Format *fmt) { Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJ_CLASS); 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); evas_font_free(obj->layer->evas->evas, fmt->font.font); free(fmt); } /** * @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(const Evas_Object *eo_obj, 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); } else { Evas_Object_Textblock_Format_Item *fi = _ITEM_FORMAT(it); if (fi->item) eina_stringshare_del(fi->item); } _format_unref_free(eo_obj, it->format); if (ln) { ln->items = (Evas_Object_Textblock_Item *) eina_inlist_remove( EINA_INLIST_GET(ln->items), EINA_INLIST_GET(ln->items)); } 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 * most common first as it's a linear search to match - no hash for this. * * these are stored as one large string and one additional array that * contains the offsets to the tokens for space efficiency. */ /** * @internal * @var escape_strings[] * This string consists of NULL terminated pairs of strings, the first of * every pair is an escape and the second is the value of the escape. */ static const char escape_strings[] = /* most common escaped stuff */ ""\0" "\x22\0" "&\0" "\x26\0" "<\0" "\x3c\0" ">\0" "\x3e\0" /* all the rest */ " \0" "\xc2\xa0\0" "¡\0" "\xc2\xa1\0" "¢\0" "\xc2\xa2\0" "£\0" "\xc2\xa3\0" "¤\0" "\xc2\xa4\0" "¥\0" "\xc2\xa5\0" "¦\0" "\xc2\xa6\0" "§\0" "\xc2\xa7\0" "¨\0" "\xc2\xa8\0" "©\0" "\xc2\xa9\0" "ª\0" "\xc2\xaa\0" "«\0" "\xc2\xab\0" "¬\0" "\xc2\xac\0" "®\0" "\xc2\xae\0" "¯\0" "\xc2\xaf\0" "°\0" "\xc2\xb0\0" "±\0" "\xc2\xb1\0" "²\0" "\xc2\xb2\0" "³\0" "\xc2\xb3\0" "´\0" "\xc2\xb4\0" "µ\0" "\xc2\xb5\0" "¶\0" "\xc2\xb6\0" "·\0" "\xc2\xb7\0" "¸\0" "\xc2\xb8\0" "¹\0" "\xc2\xb9\0" "º\0" "\xc2\xba\0" "»\0" "\xc2\xbb\0" "¼\0" "\xc2\xbc\0" "½\0" "\xc2\xbd\0" "¾\0" "\xc2\xbe\0" "¿\0" "\xc2\xbf\0" "À\0" "\xc3\x80\0" "Á\0" "\xc3\x81\0" "Â\0" "\xc3\x82\0" "Ã\0" "\xc3\x83\0" "Ä\0" "\xc3\x84\0" "Å\0" "\xc3\x85\0" "&Aelig;\0" "\xc3\x86\0" "Ç\0" "\xc3\x87\0" "È\0" "\xc3\x88\0" "É\0" "\xc3\x89\0" "Ê\0" "\xc3\x8a\0" "Ë\0" "\xc3\x8b\0" "Ì\0" "\xc3\x8c\0" "Í\0" "\xc3\x8d\0" "Î\0" "\xc3\x8e\0" "Ï\0" "\xc3\x8f\0" "&Eth;\0" "\xc3\x90\0" "Ñ\0" "\xc3\x91\0" "Ò\0" "\xc3\x92\0" "Ó\0" "\xc3\x93\0" "Ô\0" "\xc3\x94\0" "Õ\0" "\xc3\x95\0" "Ö\0" "\xc3\x96\0" "×\0" "\xc3\x97\0" "Ø\0" "\xc3\x98\0" "Ù\0" "\xc3\x99\0" "Ú\0" "\xc3\x9a\0" "Û\0" "\xc3\x9b\0" "Ý\0" "\xc3\x9d\0" "&Thorn;\0" "\xc3\x9e\0" "ß\0" "\xc3\x9f\0" "à\0" "\xc3\xa0\0" "á\0" "\xc3\xa1\0" "â\0" "\xc3\xa2\0" "ã\0" "\xc3\xa3\0" "ä\0" "\xc3\xa4\0" "å\0" "\xc3\xa5\0" "æ\0" "\xc3\xa6\0" "ç\0" "\xc3\xa7\0" "è\0" "\xc3\xa8\0" "é\0" "\xc3\xa9\0" "ê\0" "\xc3\xaa\0" "ë\0" "\xc3\xab\0" "ì\0" "\xc3\xac\0" "í\0" "\xc3\xad\0" "î\0" "\xc3\xae\0" "ï\0" "\xc3\xaf\0" "ð\0" "\xc3\xb0\0" "ñ\0" "\xc3\xb1\0" "ò\0" "\xc3\xb2\0" "ó\0" "\xc3\xb3\0" "ô\0" "\xc3\xb4\0" "õ\0" "\xc3\xb5\0" "ö\0" "\xc3\xb6\0" "÷\0" "\xc3\xb7\0" "ø\0" "\xc3\xb8\0" "ù\0" "\xc3\xb9\0" "ú\0" "\xc3\xba\0" "û\0" "\xc3\xbb\0" "ü\0" "\xc3\xbc\0" "ý\0" "\xc3\xbd\0" "þ\0" "\xc3\xbe\0" "ÿ\0" "\xc3\xbf\0" "α\0" "\xce\x91\0" "β\0" "\xce\x92\0" "γ\0" "\xce\x93\0" "δ\0" "\xce\x94\0" "ε\0" "\xce\x95\0" "ζ\0" "\xce\x96\0" "η\0" "\xce\x97\0" "θ\0" "\xce\x98\0" "ι\0" "\xce\x99\0" "κ\0" "\xce\x9a\0" "λ\0" "\xce\x9b\0" "μ\0" "\xce\x9c\0" "ν\0" "\xce\x9d\0" "ξ\0" "\xce\x9e\0" "ο\0" "\xce\x9f\0" "π\0" "\xce\xa0\0" "ρ\0" "\xce\xa1\0" "σ\0" "\xce\xa3\0" "τ\0" "\xce\xa4\0" "υ\0" "\xce\xa5\0" "φ\0" "\xce\xa6\0" "χ\0" "\xce\xa7\0" "ψ\0" "\xce\xa8\0" "ω\0" "\xce\xa9\0" "…\0" "\xe2\x80\xa6\0" "€\0" "\xe2\x82\xac\0" "←\0" "\xe2\x86\x90\0" "↑\0" "\xe2\x86\x91\0" "→\0" "\xe2\x86\x92\0" "↓\0" "\xe2\x86\x93\0" "↔\0" "\xe2\x86\x94\0" "←\0" "\xe2\x87\x90\0" "→\0" "\xe2\x87\x92\0" "∀\0" "\xe2\x88\x80\0" "∃\0" "\xe2\x88\x83\0" "∇\0" "\xe2\x88\x87\0" "∏\0" "\xe2\x88\x8f\0" "∑\0" "\xe2\x88\x91\0" "∧\0" "\xe2\x88\xa7\0" "∨\0" "\xe2\x88\xa8\0" "∫\0" "\xe2\x88\xab\0" "≠\0" "\xe2\x89\xa0\0" "≡\0" "\xe2\x89\xa1\0" "⊕\0" "\xe2\x8a\x95\0" "⊥\0" "\xe2\x8a\xa5\0" "†\0" "\xe2\x80\xa0\0" "‡\0" "\xe2\x80\xa1\0" "•\0" "\xe2\x80\xa2\0" ; /** * @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(Evas_Textblock_Cursor *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); } } /** * @internal * Returns the numeric value of HEX chars for example for ch = 'A' * the function will return 10. * * @param ch The HEX char. * @return numeric value of HEX. */ static int _hex_string_get(char ch) { if ((ch >= '0') && (ch <= '9')) return (ch - '0'); else if ((ch >= 'A') && (ch <= 'F')) return (ch - 'A' + 10); else if ((ch >= 'a') && (ch <= 'f')) return (ch - 'a' + 10); return 0; } /** * @internal * Parses a string of one of the formas: * 1. "#RRGGBB" * 2. "#RRGGBBAA" * 3. "#RGB" * 4. "#RGBA" * To the rgba values. * * @param[in] str The string to parse - NOT NULL. * @param[out] r The Red value - NOT NULL. * @param[out] g The Green value - NOT NULL. * @param[out] b The Blue value - NOT NULL. * @param[out] a The Alpha value - NOT NULL. */ static void _format_color_parse(const char *str, unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a) { int slen; slen = strlen(str); *r = *g = *b = *a = 0; if (slen == 7) /* #RRGGBB */ { *r = (_hex_string_get(str[1]) << 4) | (_hex_string_get(str[2])); *g = (_hex_string_get(str[3]) << 4) | (_hex_string_get(str[4])); *b = (_hex_string_get(str[5]) << 4) | (_hex_string_get(str[6])); *a = 0xff; } else if (slen == 9) /* #RRGGBBAA */ { *r = (_hex_string_get(str[1]) << 4) | (_hex_string_get(str[2])); *g = (_hex_string_get(str[3]) << 4) | (_hex_string_get(str[4])); *b = (_hex_string_get(str[5]) << 4) | (_hex_string_get(str[6])); *a = (_hex_string_get(str[7]) << 4) | (_hex_string_get(str[8])); } else if (slen == 4) /* #RGB */ { *r = _hex_string_get(str[1]); *r = (*r << 4) | *r; *g = _hex_string_get(str[2]); *g = (*g << 4) | *g; *b = _hex_string_get(str[3]); *b = (*b << 4) | *b; *a = 0xff; } else if (slen == 5) /* #RGBA */ { *r = _hex_string_get(str[1]); *r = (*r << 4) | *r; *g = _hex_string_get(str[2]); *g = (*g << 4) | *g; *b = _hex_string_get(str[3]); *b = (*b << 4) | *b; *a = _hex_string_get(str[4]); *a = (*a << 4) | *a; } *r = (*r * *a) / 255; *g = (*g * *a) / 255; *b = (*b * *a) / 255; } /* 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 *underline_dash_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 *backing_colorstr = NULL; static const char *strikethrough_colorstr = NULL; static const char *alignstr = NULL; static const char *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 *strikethroughstr = NULL; static const char *backingstr = NULL; static const char *stylestr = NULL; static const char *tabstopsstr = NULL; static const char *linesizestr = NULL; static const char *linerelsizestr = NULL; static const char *linegapstr = NULL; static const char *linerelgapstr = NULL; static const char *itemstr = NULL; static const char *linefillstr = NULL; static const char *ellipsisstr = NULL; static const char *passwordstr = NULL; static const char *underline_dash_widthstr = NULL; static const char *underline_dash_gapstr = NULL; /** * @page evas_textblock_style_page Evas Textblock Style Options * * @brief This page describes how to style text in an Evas Text Block. */ /** * @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 * * @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"); underline_dash_colorstr = eina_stringshare_add("underline_dash_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"); backing_colorstr = eina_stringshare_add("backing_color"); strikethrough_colorstr = eina_stringshare_add("strikethrough_color"); alignstr = eina_stringshare_add("align"); valignstr = eina_stringshare_add("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"); strikethroughstr = eina_stringshare_add("strikethrough"); backingstr = eina_stringshare_add("backing"); stylestr = eina_stringshare_add("style"); tabstopsstr = eina_stringshare_add("tabstops"); linesizestr = eina_stringshare_add("linesize"); linerelsizestr = eina_stringshare_add("linerelsize"); linegapstr = eina_stringshare_add("linegap"); linerelgapstr = eina_stringshare_add("linerelgap"); itemstr = eina_stringshare_add("item"); linefillstr = eina_stringshare_add("linefill"); ellipsisstr = eina_stringshare_add("ellipsis"); passwordstr = eina_stringshare_add("password"); underline_dash_widthstr = eina_stringshare_add("underline_dash_width"); underline_dash_gapstr = eina_stringshare_add("underline_dash_gap"); } 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(underline_dash_colorstr); eina_stringshare_del(outline_colorstr); eina_stringshare_del(shadow_colorstr); eina_stringshare_del(glow_colorstr); eina_stringshare_del(glow2_colorstr); eina_stringshare_del(backing_colorstr); eina_stringshare_del(strikethrough_colorstr); eina_stringshare_del(alignstr); eina_stringshare_del(valignstr); eina_stringshare_del(wrapstr); eina_stringshare_del(left_marginstr); eina_stringshare_del(right_marginstr); eina_stringshare_del(underlinestr); eina_stringshare_del(strikethroughstr); eina_stringshare_del(backingstr); eina_stringshare_del(stylestr); eina_stringshare_del(tabstopsstr); eina_stringshare_del(linesizestr); eina_stringshare_del(linerelsizestr); eina_stringshare_del(linegapstr); eina_stringshare_del(linerelgapstr); eina_stringshare_del(itemstr); eina_stringshare_del(linefillstr); eina_stringshare_del(ellipsisstr); eina_stringshare_del(passwordstr); eina_stringshare_del(underline_dash_widthstr); eina_stringshare_del(underline_dash_gapstr); } /** * @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 void _format_clean_param(char *dst, const char *src) { const char *ss; char *ds; ds = dst; for (ss = src; *ss; ss++, ds++) { if ((*ss == '\\') && *(ss + 1)) ss++; *ds = *ss; } *ds = 0; } /** * @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. */ static void _format_command(Evas_Object *eo_obj, Evas_Object_Textblock_Format *fmt, const char *cmd, const char *param) { int len; char *tmp_param; len = strlen(param); tmp_param = alloca(len + 1); _format_clean_param(tmp_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(); } 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, tmp_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), tmp_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(tmp_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, tmp_param)))) { if (fmt->font.source) eina_stringshare_del(fmt->font.source); fmt->font.source = eina_stringshare_add(tmp_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 "light" * @li "book" * @li "medium" * @li "semibold" * @li "bold" * @li "ultrabold" * @li "black" * @li "extrablack" * @code * font_weight= * @endcode */ fmt->font.fdesc->weight = evas_font_style_find(tmp_param, tmp_param + strlen(tmp_param), 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=