aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/evas/src/lib/canvas/evas_object_textblock.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/evas/src/lib/canvas/evas_object_textblock.c')
-rw-r--r--libraries/evas/src/lib/canvas/evas_object_textblock.c909
1 files changed, 648 insertions, 261 deletions
diff --git a/libraries/evas/src/lib/canvas/evas_object_textblock.c b/libraries/evas/src/lib/canvas/evas_object_textblock.c
index 7941a45..568911c 100644
--- a/libraries/evas/src/lib/canvas/evas_object_textblock.c
+++ b/libraries/evas/src/lib/canvas/evas_object_textblock.c
@@ -61,13 +61,13 @@
61 * @subsection textblock_layout The layout system 61 * @subsection textblock_layout The layout system
62 * @todo write @ref textblock_layout 62 * @todo write @ref textblock_layout
63 */ 63 */
64#include <stdlib.h>
65
66#include "evas_common.h" 64#include "evas_common.h"
67#include "evas_private.h" 65#include "evas_private.h"
66#include <stdlib.h>
68 67
69#ifdef HAVE_LINEBREAK 68#ifdef HAVE_LINEBREAK
70#include "linebreak.h" 69#include "linebreak.h"
70#include "wordbreak.h"
71#endif 71#endif
72 72
73/* save typing */ 73/* save typing */
@@ -78,12 +78,19 @@
78static const char o_type[] = "textblock"; 78static const char o_type[] = "textblock";
79 79
80/* The char to be inserted instead of visible formats */ 80/* The char to be inserted instead of visible formats */
81#define EVAS_TEXTBLOCK_REPLACEMENT_CHAR 0xFFFC 81#define _REPLACEMENT_CHAR 0xFFFC
82#define _PARAGRAPH_SEPARATOR 0x2029 82#define _PARAGRAPH_SEPARATOR 0x2029
83#define _NEWLINE '\n'
84#define _TAB '\t'
85
86#define _REPLACEMENT_CHAR_UTF8 "\xEF\xBF\xBC"
87#define _PARAGRAPH_SEPARATOR_UTF8 "\xE2\x80\xA9"
88#define _NEWLINE_UTF8 "\n"
89#define _TAB_UTF8 "\t"
83#define EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(ch) \ 90#define EVAS_TEXTBLOCK_IS_VISIBLE_FORMAT_CHAR(ch) \
84 (((ch) == EVAS_TEXTBLOCK_REPLACEMENT_CHAR) || \ 91 (((ch) == _REPLACEMENT_CHAR) || \
85 ((ch) == '\n') || \ 92 ((ch) == _NEWLINE) || \
86 ((ch) == '\t') || \ 93 ((ch) == _TAB) || \
87 ((ch) == _PARAGRAPH_SEPARATOR)) 94 ((ch) == _PARAGRAPH_SEPARATOR))
88 95
89/* private struct for textblock object internal data */ 96/* private struct for textblock object internal data */
@@ -101,6 +108,12 @@ typedef struct _Evas_Object_Textblock Evas_Object_Textblock;
101typedef struct _Evas_Object_Style_Tag Evas_Object_Style_Tag; 108typedef struct _Evas_Object_Style_Tag Evas_Object_Style_Tag;
102/** 109/**
103 * @internal 110 * @internal
111 * @typedef Evas_Object_Style_Tag
112 * The structure used for finding style tags.
113 */
114typedef struct _Evas_Object_Style_Tag_Base Evas_Object_Style_Tag_Base;
115/**
116 * @internal
104 * @typedef Evas_Object_Textblock_Node_Text 117 * @typedef Evas_Object_Textblock_Node_Text
105 * A text node. 118 * A text node.
106 */ 119 */
@@ -195,21 +208,26 @@ typedef struct _Evas_Object_Textblock_Format Evas_Object_Textblock_Format;
195 * Returns true if closer is the closer of base. 208 * Returns true if closer is the closer of base.
196 */ 209 */
197#define _FORMAT_IS_CLOSER_OF(base, closer, closer_len) \ 210#define _FORMAT_IS_CLOSER_OF(base, closer, closer_len) \
198 (!strncmp(base + 1, closer, closer_len) && \ 211 (!strncmp(base, closer, closer_len) && \
199 (!base[closer_len + 1] || \ 212 (!base[closer_len] || \
200 (base[closer_len + 1] == '=') || \ 213 (base[closer_len] == '=') || \
201 _is_white(base[closer_len + 1]))) 214 _is_white(base[closer_len])))
202 215
203/*FIXME: document the structs and struct items. */ 216/*FIXME: document the structs and struct items. */
204struct _Evas_Object_Style_Tag 217struct _Evas_Object_Style_Tag_Base
205{ 218{
206 EINA_INLIST;
207 char *tag; 219 char *tag;
208 char *replace; 220 char *replace;
209 size_t tag_len; 221 size_t tag_len;
210 size_t replace_len; 222 size_t replace_len;
211}; 223};
212 224
225struct _Evas_Object_Style_Tag
226{
227 EINA_INLIST;
228 Evas_Object_Style_Tag_Base tag;
229};
230
213struct _Evas_Object_Textblock_Node_Text 231struct _Evas_Object_Textblock_Node_Text
214{ 232{
215 EINA_INLIST; 233 EINA_INLIST;
@@ -229,11 +247,18 @@ struct _Evas_Object_Textblock_Node_Format
229 Evas_Object_Textblock_Node_Text *text_node; 247 Evas_Object_Textblock_Node_Text *text_node;
230 size_t offset; 248 size_t offset;
231 unsigned char anchor : 2; 249 unsigned char anchor : 2;
250 Eina_Bool opener : 1;
251 Eina_Bool own_closer : 1;
232 Eina_Bool visible : 1; 252 Eina_Bool visible : 1;
233 Eina_Bool format_change : 1; 253 Eina_Bool format_change : 1;
234 Eina_Bool is_new : 1; 254 Eina_Bool is_new : 1;
235}; 255};
236 256
257/* The default tags to use */
258static const Evas_Object_Style_Tag_Base default_tags[] = {
259 { "b", "+ font_weight=Bold", 1, 18 },
260 { "i", "+ font_style=Italic", 1, 19 }};
261
237#define ANCHOR_NONE 0 262#define ANCHOR_NONE 0
238#define ANCHOR_A 1 263#define ANCHOR_A 1
239#define ANCHOR_ITEM 2 264#define ANCHOR_ITEM 2
@@ -538,8 +563,8 @@ _style_replace(Evas_Textblock_Style *ts, const char *style_text)
538 563
539 tag = (Evas_Object_Style_Tag *)ts->tags; 564 tag = (Evas_Object_Style_Tag *)ts->tags;
540 ts->tags = (Evas_Object_Style_Tag *)eina_inlist_remove(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag)); 565 ts->tags = (Evas_Object_Style_Tag *)eina_inlist_remove(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
541 free(tag->tag); 566 free(tag->tag.tag);
542 free(tag->replace); 567 free(tag->tag.replace);
543 free(tag); 568 free(tag);
544 } 569 }
545 ts->default_tag = NULL; 570 ts->default_tag = NULL;
@@ -567,19 +592,38 @@ _style_clear(Evas_Textblock_Style *ts)
567 * @return The replacement string found. 592 * @return The replacement string found.
568 */ 593 */
569static inline const char * 594static inline const char *
570_style_match_tag(Evas_Textblock_Style *ts, const char *s, size_t tag_len, size_t *replace_len) 595_style_match_tag(const Evas_Textblock_Style *ts, const char *s, size_t tag_len, size_t *replace_len)
571{ 596{
572 Evas_Object_Style_Tag *tag; 597 Evas_Object_Style_Tag *tag;
573 598
599 /* Try the style tags */
574 EINA_INLIST_FOREACH(ts->tags, tag) 600 EINA_INLIST_FOREACH(ts->tags, tag)
575 { 601 {
576 if (tag->tag_len != tag_len) continue; 602 if (tag->tag.tag_len != tag_len) continue;
577 if (!strncmp(tag->tag, s, tag_len)) 603 if (!strncmp(tag->tag.tag, s, tag_len))
578 { 604 {
579 *replace_len = tag->replace_len; 605 *replace_len = tag->tag.replace_len;
580 return tag->replace; 606 return tag->tag.replace;
581 } 607 }
582 } 608 }
609
610 /* Try the default tags */
611 {
612 size_t i;
613 const Evas_Object_Style_Tag_Base *btag;
614 for (btag = default_tags, i = 0 ;
615 i < (sizeof(default_tags) / sizeof(default_tags[0])) ;
616 btag++, i++)
617 {
618 if (btag->tag_len != tag_len) continue;
619 if (!strncmp(btag->tag, s, tag_len))
620 {
621 *replace_len = btag->replace_len;
622 return btag->replace;
623 }
624 }
625 }
626
583 *replace_len = 0; 627 *replace_len = 0;
584 return NULL; 628 return NULL;
585} 629}
@@ -1689,8 +1733,8 @@ _format_fill(Evas_Object *obj, Evas_Object_Textblock_Format *fmt, const char *st
1689 1733
1690 s = str; 1734 s = str;
1691 1735
1692 /* get rid of anything +s or -s off the start of the string */ 1736 /* get rid of any spaces at the start of the string */
1693 while ((*s == ' ') || (*s == '+') || (*s == '-')) s++; 1737 while (*s == ' ') s++;
1694 1738
1695 while ((item = _format_parse(&s))) 1739 while ((item = _format_parse(&s)))
1696 { 1740 {
@@ -2131,7 +2175,6 @@ _layout_format_pop(Ctxt *c, const char *format)
2131 if ((c->format_stack) && (c->format_stack->next)) 2175 if ((c->format_stack) && (c->format_stack->next))
2132 { 2176 {
2133 Eina_List *redo_nodes = NULL; 2177 Eina_List *redo_nodes = NULL;
2134 format++; /* Skip the '-' */
2135 2178
2136 /* Generic pop, should just pop. */ 2179 /* Generic pop, should just pop. */
2137 if (((format[0] == ' ') && !format[1]) || 2180 if (((format[0] == ' ') && !format[1]) ||
@@ -2994,21 +3037,29 @@ _format_finalize(Evas_Object *obj, Evas_Object_Textblock_Format *fmt)
2994 * @def _IS_TAB(item) 3037 * @def _IS_TAB(item)
2995 */ 3038 */
2996#define _IS_TAB(item) \ 3039#define _IS_TAB(item) \
2997 (!strcmp(item, "\t") || !strcmp(item, "\\t")) 3040 (!strcmp(item, "tab") || !strcmp(item, "\t") || !strcmp(item, "\\t"))
2998/** 3041/**
2999 * @internal 3042 * @internal
3000 * Returns true if the item is a line spearator, false otherwise 3043 * Returns true if the item is a line spearator, false otherwise
3001 * @def _IS_LINE_SEPARATOR(item) 3044 * @def _IS_LINE_SEPARATOR(item)
3002 */ 3045 */
3003#define _IS_LINE_SEPARATOR(item) \ 3046#define _IS_LINE_SEPARATOR(item) \
3004 (!strcmp(item, "\n") || !strcmp(item, "\\n")) 3047 (!strcmp(item, "br") || !strcmp(item, "\n") || !strcmp(item, "\\n"))
3005/** 3048/**
3006 * @internal 3049 * @internal
3007 * Returns true if the item is a paragraph separator, false otherwise 3050 * Returns true if the item is a paragraph separator, false otherwise
3008 * @def _IS_PARAGRAPH_SEPARATOR(item) 3051 * @def _IS_PARAGRAPH_SEPARATOR(item)
3009 */ 3052 */
3053#define _IS_PARAGRAPH_SEPARATOR_SIMPLE(item) \
3054 (!strcmp(item, "ps"))
3055/**
3056 * @internal
3057 * Returns true if the item is a paragraph separator, false otherwise
3058 * takes legacy mode into account.
3059 * @def _IS_PARAGRAPH_SEPARATOR(item)
3060 */
3010#define _IS_PARAGRAPH_SEPARATOR(o, item) \ 3061#define _IS_PARAGRAPH_SEPARATOR(o, item) \
3011 (!strcmp(item, "ps") || \ 3062 (_IS_PARAGRAPH_SEPARATOR_SIMPLE(item) || \
3012 (o->legacy_newline && _IS_LINE_SEPARATOR(item))) /* Paragraph separator */ 3063 (o->legacy_newline && _IS_LINE_SEPARATOR(item))) /* Paragraph separator */
3013 3064
3014/** 3065/**
@@ -3041,7 +3092,7 @@ _layout_do_format(const Evas_Object *obj __UNUSED__, Ctxt *c,
3041 int handled = 0; 3092 int handled = 0;
3042 3093
3043 s = n->format; 3094 s = n->format;
3044 if (!strncmp(s, "+ item ", 7)) 3095 if (!strncmp(s, "item ", 5))
3045 { 3096 {
3046 // one of: 3097 // one of:
3047 // item size=20x10 href=name 3098 // item size=20x10 href=name
@@ -3133,16 +3184,14 @@ _layout_do_format(const Evas_Object *obj __UNUSED__, Ctxt *c,
3133 if (!handled) 3184 if (!handled)
3134 { 3185 {
3135 Eina_Bool push_fmt = EINA_FALSE; 3186 Eina_Bool push_fmt = EINA_FALSE;
3136 if (s[0] == '+') 3187 if (n->opener && !n->own_closer)
3137 { 3188 {
3138 fmt = _layout_format_push(c, fmt, n); 3189 fmt = _layout_format_push(c, fmt, n);
3139 s++;
3140 push_fmt = EINA_TRUE; 3190 push_fmt = EINA_TRUE;
3141 } 3191 }
3142 else if (s[0] == '-') 3192 else if (!n->opener)
3143 { 3193 {
3144 fmt = _layout_format_pop(c, n->orig_format); 3194 fmt = _layout_format_pop(c, n->orig_format);
3145 s++;
3146 } 3195 }
3147 while ((item = _format_parse(&s))) 3196 while ((item = _format_parse(&s)))
3148 { 3197 {
@@ -3166,7 +3215,7 @@ _layout_do_format(const Evas_Object *obj __UNUSED__, Ctxt *c,
3166 3215
3167 fi->parent.w = fi->parent.adv = 0; 3216 fi->parent.w = fi->parent.adv = 0;
3168 } 3217 }
3169 else if ((!strcmp(item, "\t")) || (!strcmp(item, "\\t"))) 3218 else if (_IS_TAB(item))
3170 { 3219 {
3171 Evas_Object_Textblock_Format_Item *fi; 3220 Evas_Object_Textblock_Format_Item *fi;
3172 3221
@@ -3215,28 +3264,33 @@ _layout_update_par(Ctxt *c)
3215/* -1 means no wrap */ 3264/* -1 means no wrap */
3216static int 3265static int
3217_layout_get_charwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt, 3266_layout_get_charwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3218 const Evas_Object_Textblock_Text_Item *ti, size_t line_start, 3267 const Evas_Object_Textblock_Item *it, size_t line_start,
3219 const char *breaks) 3268 const char *breaks)
3220{ 3269{
3221 int wrap; 3270 int wrap;
3222 size_t uwrap; 3271 size_t uwrap;
3223 size_t len = eina_ustrbuf_length_get(ti->parent.text_node->unicode); 3272 size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
3224 /* Currently not being used, because it doesn't contain relevant 3273 /* Currently not being used, because it doesn't contain relevant
3225 * information */ 3274 * information */
3226 (void) breaks; 3275 (void) breaks;
3227 3276
3228 { 3277 {
3229 wrap = _layout_text_cutoff_get(c, fmt, ti); 3278 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3279 wrap = 0;
3280 else
3281 wrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it));
3282
3230 if (wrap < 0) 3283 if (wrap < 0)
3231 return -1; 3284 return -1;
3232 uwrap = (size_t) wrap + ti->parent.text_pos; 3285 uwrap = (size_t) wrap + it->text_pos;
3233 } 3286 }
3234 3287
3235 3288
3236 if (uwrap == line_start) 3289 if ((uwrap == line_start) && (it->type == EVAS_TEXTBLOCK_ITEM_TEXT))
3237 { 3290 {
3238 uwrap = ti->parent.text_pos + 3291 uwrap = it->text_pos +
3239 (size_t) evas_common_text_props_cluster_next(&ti->text_props, wrap); 3292 (size_t) evas_common_text_props_cluster_next(
3293 &_ITEM_TEXT(it)->text_props, wrap);
3240 } 3294 }
3241 if ((uwrap <= line_start) || (uwrap > len)) 3295 if ((uwrap <= line_start) || (uwrap > len))
3242 return -1; 3296 return -1;
@@ -3259,16 +3313,16 @@ _layout_get_charwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3259#endif 3313#endif
3260static int 3314static int
3261_layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt, 3315_layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3262 const Evas_Object_Textblock_Text_Item *ti, Eina_Bool mixed_wrap, 3316 const Evas_Object_Textblock_Item *it, Eina_Bool mixed_wrap,
3263 size_t line_start, const char *breaks) 3317 size_t line_start, const char *breaks)
3264{ 3318{
3265 Eina_Bool wrap_after = EINA_FALSE; 3319 Eina_Bool wrap_after = EINA_FALSE;
3266 size_t wrap; 3320 size_t wrap;
3267 size_t orig_wrap; 3321 size_t orig_wrap;
3268 const Eina_Unicode *str = eina_ustrbuf_string_get( 3322 const Eina_Unicode *str = eina_ustrbuf_string_get(
3269 ti->parent.text_node->unicode); 3323 it->text_node->unicode);
3270 int item_start = ti->parent.text_pos; 3324 int item_start = it->text_pos;
3271 size_t len = eina_ustrbuf_length_get(ti->parent.text_node->unicode); 3325 size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
3272#ifndef HAVE_LINEBREAK 3326#ifndef HAVE_LINEBREAK
3273 /* Not used without liblinebreak ATM. */ 3327 /* Not used without liblinebreak ATM. */
3274 (void) breaks; 3328 (void) breaks;
@@ -3276,7 +3330,10 @@ _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3276 3330
3277 { 3331 {
3278 int swrap = -1; 3332 int swrap = -1;
3279 swrap = _layout_text_cutoff_get(c, fmt, ti); 3333 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
3334 swrap = 0;
3335 else
3336 swrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it));
3280 /* Avoiding too small textblocks to even contain one char. 3337 /* Avoiding too small textblocks to even contain one char.
3281 * FIXME: This can cause breaking inside ligatures. */ 3338 * FIXME: This can cause breaking inside ligatures. */
3282 3339
@@ -3331,7 +3388,7 @@ _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3331 { 3388 {
3332 if (mixed_wrap) 3389 if (mixed_wrap)
3333 { 3390 {
3334 return _layout_get_charwrap(c, fmt, ti, 3391 return _layout_get_charwrap(c, fmt, it,
3335 line_start, breaks); 3392 line_start, breaks);
3336 } 3393 }
3337 else 3394 else
@@ -3362,20 +3419,20 @@ _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3362/* -1 means no wrap */ 3419/* -1 means no wrap */
3363static int 3420static int
3364_layout_get_wordwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt, 3421_layout_get_wordwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3365 const Evas_Object_Textblock_Text_Item *ti, size_t line_start, 3422 const Evas_Object_Textblock_Item *it, size_t line_start,
3366 const char *breaks) 3423 const char *breaks)
3367{ 3424{
3368 return _layout_get_word_mixwrap_common(c, fmt, ti, EINA_FALSE, line_start, 3425 return _layout_get_word_mixwrap_common(c, fmt, it, EINA_FALSE, line_start,
3369 breaks); 3426 breaks);
3370} 3427}
3371 3428
3372/* -1 means no wrap */ 3429/* -1 means no wrap */
3373static int 3430static int
3374_layout_get_mixedwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt, 3431_layout_get_mixedwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
3375 const Evas_Object_Textblock_Text_Item *ti, size_t line_start, 3432 const Evas_Object_Textblock_Item *it, size_t line_start,
3376 const char *breaks) 3433 const char *breaks)
3377{ 3434{
3378 return _layout_get_word_mixwrap_common(c, fmt, ti, EINA_TRUE, line_start, 3435 return _layout_get_word_mixwrap_common(c, fmt, it, EINA_TRUE, line_start,
3379 breaks); 3436 breaks);
3380} 3437}
3381 3438
@@ -3503,7 +3560,7 @@ _layout_paragraph_reorder_lines(Evas_Object_Textblock_Paragraph *par)
3503 3560
3504static void 3561static void
3505_layout_paragraph_render(Evas_Object_Textblock *o, 3562_layout_paragraph_render(Evas_Object_Textblock *o,
3506 Evas_Object_Textblock_Paragraph *par) 3563 Evas_Object_Textblock_Paragraph *par)
3507{ 3564{
3508 if (par->rendered) 3565 if (par->rendered)
3509 return; 3566 return;
@@ -3521,6 +3578,8 @@ _layout_paragraph_render(Evas_Object_Textblock *o,
3521 par->bidi_props = NULL; 3578 par->bidi_props = NULL;
3522 } 3579 }
3523 } 3580 }
3581#else
3582 (void) o;
3524#endif 3583#endif
3525} 3584}
3526 3585
@@ -3656,120 +3715,127 @@ _layout_par(Ctxt *c)
3656 else if ((it->format->wrap_word || it->format->wrap_char || 3715 else if ((it->format->wrap_word || it->format->wrap_char ||
3657 it->format->wrap_mixed) && it->text_node) 3716 it->format->wrap_mixed) && it->text_node)
3658 { 3717 {
3659 if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) 3718 size_t line_start;
3719 size_t it_len;
3720
3721 it_len = (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT) ?
3722 1 : _ITEM_TEXT(it)->text_props.text_len;
3723
3724
3725#ifdef HAVE_LINEBREAK
3726 /* If we haven't calculated the linebreaks yet,
3727 * do */
3728 if (!line_breaks)
3660 { 3729 {
3661 /* Don't wrap if it's the only item */ 3730 /* Only relevant in those cases */
3662 if (c->ln->items) 3731 if (it->format->wrap_word || it->format->wrap_mixed)
3663 { 3732 {
3664 /*FIXME: I should handle format correctly, 3733 const char *lang;
3665 i.e verify we are allowed to break here */ 3734 lang = (it->format->font.fdesc) ?
3666 _layout_line_advance(c, it->format); 3735 it->format->font.fdesc->lang : "";
3667 wrap = -1; 3736 size_t len =
3737 eina_ustrbuf_length_get(
3738 it->text_node->unicode);
3739 line_breaks = malloc(len);
3740 set_linebreaks_utf32((const utf32_t *)
3741 eina_ustrbuf_string_get(
3742 it->text_node->unicode),
3743 len, lang, line_breaks);
3668 } 3744 }
3669 } 3745 }
3746#endif
3747 if (c->ln->items)
3748 line_start = c->ln->items->text_pos;
3670 else 3749 else
3671 { 3750 line_start = it->text_pos;
3672 Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
3673 size_t line_start;
3674 3751
3675#ifdef HAVE_LINEBREAK 3752 adv_line = 1;
3676 /* If we haven't calculated the linebreaks yet, 3753 /* If we don't already have a wrap point from before */
3677 * do */ 3754 if (wrap < 0)
3678 if (!line_breaks) 3755 {
3679 { 3756 if (it->format->wrap_word)
3680 /* Only relevant in those cases */ 3757 wrap = _layout_get_wordwrap(c, it->format, it,
3681 if (it->format->wrap_word || it->format->wrap_mixed) 3758 line_start, line_breaks);
3682 { 3759 else if (it->format->wrap_char)
3683 const char *lang; 3760 wrap = _layout_get_charwrap(c, it->format, it,
3684 lang = (it->format->font.fdesc) ? 3761 line_start, line_breaks);
3685 it->format->font.fdesc->lang : ""; 3762 else if (it->format->wrap_mixed)
3686 size_t len = 3763 wrap = _layout_get_mixedwrap(c, it->format, it,
3687 eina_ustrbuf_length_get( 3764 line_start, line_breaks);
3688 it->text_node->unicode);
3689 line_breaks = malloc(len);
3690 set_linebreaks_utf32((const utf32_t *)
3691 eina_ustrbuf_string_get(
3692 it->text_node->unicode),
3693 len, lang, line_breaks);
3694 }
3695 }
3696#endif
3697 if (c->ln->items)
3698 line_start = c->ln->items->text_pos;
3699 else 3765 else
3700 line_start = ti->parent.text_pos; 3766 wrap = -1;
3701 3767 }
3702 adv_line = 1;
3703 /* If we don't already have a wrap point from before */
3704 if (wrap < 0)
3705 {
3706 if (it->format->wrap_word)
3707 wrap = _layout_get_wordwrap(c, it->format, ti,
3708 line_start, line_breaks);
3709 else if (it->format->wrap_char)
3710 wrap = _layout_get_charwrap(c, it->format, ti,
3711 line_start, line_breaks);
3712 else if (it->format->wrap_mixed)
3713 wrap = _layout_get_mixedwrap(c, it->format, ti,
3714 line_start, line_breaks);
3715 else
3716 wrap = -1;
3717 }
3718 3768
3719 /* If it's before the item, rollback and apply. 3769 /* If it's before the item, rollback and apply.
3720 if it's in the item, cut. 3770 if it's in the item, cut.
3721 If it's after the item, delay the cut */ 3771 If it's after the item, delay the cut */
3722 if (wrap > 0) 3772 if (wrap > 0)
3773 {
3774 size_t uwrap = (size_t) wrap;
3775 if (uwrap < it->text_pos)
3723 { 3776 {
3724 size_t uwrap = (size_t) wrap; 3777 /* Rollback latest additions, and cut that
3725 if (uwrap < ti->parent.text_pos) 3778 item */
3779 i = eina_list_prev(i);
3780 it = eina_list_data_get(i);
3781 while (uwrap < it->text_pos)
3726 { 3782 {
3727 /* Rollback latest additions, and cut that
3728 item */
3729 i = eina_list_prev(i);
3730 it = eina_list_data_get(i);
3731 while (uwrap < it->text_pos)
3732 {
3733 c->ln->items = _ITEM(
3734 eina_inlist_remove(
3735 EINA_INLIST_GET(c->ln->items),
3736 EINA_INLIST_GET(it)));
3737 i = eina_list_prev(i);
3738 it = eina_list_data_get(i);
3739 }
3740 c->x = it->x;
3741 c->ln->items = _ITEM( 3783 c->ln->items = _ITEM(
3742 eina_inlist_remove( 3784 eina_inlist_remove(
3743 EINA_INLIST_GET(c->ln->items), 3785 EINA_INLIST_GET(c->ln->items),
3744 EINA_INLIST_GET(it))); 3786 EINA_INLIST_GET(it)));
3745 continue; 3787 i = eina_list_prev(i);
3788 it = eina_list_data_get(i);
3746 } 3789 }
3747 /* If it points to the end, it means the previous 3790 c->x = it->x;
3748 * char is a whitespace we should remove, so this 3791 c->ln->items = _ITEM(
3749 * is a wanted cutting point. */ 3792 eina_inlist_remove(
3750 else if (uwrap > ti->parent.text_pos + 3793 EINA_INLIST_GET(c->ln->items),
3751 ti->text_props.text_len) 3794 EINA_INLIST_GET(it)));
3752 wrap = -1; /* Delay the cut in a smart way 3795 continue;
3753 i.e use the item_pos as the line_start, because
3754 there's already no cut before*/
3755 else
3756 wrap -= ti->parent.text_pos; /* Cut here */
3757 } 3796 }
3758 3797 /* If it points to the end, it means the previous
3759 if (wrap > 0) 3798 * char is a whitespace we should remove, so this
3799 * is a wanted cutting point. */
3800 else if (uwrap > it->text_pos + it_len)
3760 { 3801 {
3761 _layout_item_text_split_strip_white(c, ti, i, wrap); 3802 /* FIXME: Should redo the ellipsis handling.
3762 } 3803 * If we can do ellipsis, just cut here. */
3763 else if (wrap == 0) 3804 if (it->format->ellipsis == 1.0)
3805 {
3806 _layout_handle_ellipsis(c, it, i);
3807 ret = 1;
3808 goto end;
3809 }
3810 else
3811 {
3812 /* Delay the cut in a smart way i.e use the
3813 item_pos as the line_start, because
3814 there's already no cut before*/
3815 wrap = -1;
3816 }
3817 }
3818 else
3819 wrap -= it->text_pos; /* Cut here */
3820 }
3821
3822 if (wrap > 0)
3823 {
3824 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
3764 { 3825 {
3765 /* Should wrap before the item */ 3826 _layout_item_text_split_strip_white(c,
3766 adv_line = 0; 3827 _ITEM_TEXT(it), i, wrap);
3767 redo_item = 1;
3768 _layout_line_advance(c, it->format);
3769 } 3828 }
3770 /* Reset wrap */
3771 wrap = -1;
3772 } 3829 }
3830 else if (wrap == 0)
3831 {
3832 /* Should wrap before the item */
3833 adv_line = 0;
3834 redo_item = 1;
3835 _layout_line_advance(c, it->format);
3836 }
3837 /* Reset wrap */
3838 wrap = -1;
3773 } 3839 }
3774 } 3840 }
3775 3841
@@ -3844,18 +3910,16 @@ _format_changes_invalidate_text_nodes(Ctxt *c)
3844 const char *fstr = fnode->orig_format; 3910 const char *fstr = fnode->orig_format;
3845 /* balance < 0 means we gave up and everything should be 3911 /* balance < 0 means we gave up and everything should be
3846 * invalidated */ 3912 * invalidated */
3847 if (*fstr == '+') 3913 if (fnode->opener && !fnode->own_closer)
3848 { 3914 {
3849 balance++; 3915 balance++;
3850 if (!fstack) 3916 if (!fstack)
3851 start_n = fnode->text_node; 3917 start_n = fnode->text_node;
3852 fstack = eina_list_prepend(fstack, fnode); 3918 fstack = eina_list_prepend(fstack, fnode);
3853 } 3919 }
3854 else if (*fstr == '-') 3920 else if (!fnode->opener)
3855 { 3921 {
3856 size_t fstr_len; 3922 size_t fstr_len;
3857 /* Skip the '-' */
3858 fstr++;
3859 fstr_len = strlen(fstr); 3923 fstr_len = strlen(fstr);
3860 /* Generic popper, just pop */ 3924 /* Generic popper, just pop */
3861 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0]) 3925 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
@@ -4429,12 +4493,12 @@ evas_textblock_style_set(Evas_Textblock_Style *ts, const char *text)
4429 { 4493 {
4430 if (!key_start) 4494 if (!key_start)
4431 { 4495 {
4432 if (!isspace(*p)) 4496 if (!isspace((unsigned char)(*p)))
4433 key_start = p; 4497 key_start = p;
4434 } 4498 }
4435 else if (!key_stop) 4499 else if (!key_stop)
4436 { 4500 {
4437 if ((*p == '=') || (isspace(*p))) 4501 if ((*p == '=') || (isspace((unsigned char)(*p))))
4438 key_stop = p; 4502 key_stop = p;
4439 } 4503 }
4440 else if (!val_start) 4504 else if (!val_start)
@@ -4479,10 +4543,10 @@ evas_textblock_style_set(Evas_Textblock_Style *ts, const char *text)
4479 tag = calloc(1, sizeof(Evas_Object_Style_Tag)); 4543 tag = calloc(1, sizeof(Evas_Object_Style_Tag));
4480 if (tag) 4544 if (tag)
4481 { 4545 {
4482 tag->tag = tags; 4546 tag->tag.tag = tags;
4483 tag->replace = replaces; 4547 tag->tag.replace = replaces;
4484 tag->tag_len = tag_len; 4548 tag->tag.tag_len = tag_len;
4485 tag->replace_len = replace_len; 4549 tag->tag.replace_len = replace_len;
4486 ts->tags = (Evas_Object_Style_Tag *)eina_inlist_append(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag)); 4550 ts->tags = (Evas_Object_Style_Tag *)eina_inlist_append(EINA_INLIST_GET(ts->tags), EINA_INLIST_GET(tag));
4487 } 4551 }
4488 else 4552 else
@@ -4689,7 +4753,11 @@ _escaped_char_match(const char *s, int *adv)
4689 while ((*mc) && (*sc)) 4753 while ((*mc) && (*sc))
4690 { 4754 {
4691 if ((unsigned char)*sc < (unsigned char)*mc) return NULL; 4755 if ((unsigned char)*sc < (unsigned char)*mc) return NULL;
4692 if (*sc != *mc) match = 0; 4756 if (*sc != *mc)
4757 {
4758 match = 0;
4759 break;
4760 }
4693 mc++; 4761 mc++;
4694 sc++; 4762 sc++;
4695 } 4763 }
@@ -4723,7 +4791,7 @@ _escaped_char_get(const char *s, const char *s_end)
4723 int base = 10; 4791 int base = 10;
4724 s += 2; /* Skip "&#" */ 4792 s += 2; /* Skip "&#" */
4725 4793
4726 if (tolower(*s) == 'x') 4794 if (tolower((unsigned char)(*s)) == 'x')
4727 { 4795 {
4728 s++; 4796 s++;
4729 base = 16; 4797 base = 16;
@@ -4876,6 +4944,7 @@ evas_object_textblock_text_markup_prepend(Evas_Textblock_Cursor *cur, const char
4876 * NULL is reached. */ 4944 * NULL is reached. */
4877 for (;;) 4945 for (;;)
4878 { 4946 {
4947 size_t text_len;
4879 /* If we got to the end of string or just finished/started tag 4948 /* If we got to the end of string or just finished/started tag
4880 * or escape sequence handling. */ 4949 * or escape sequence handling. */
4881 if ((*p == 0) || 4950 if ((*p == 0) ||
@@ -4953,14 +5022,22 @@ evas_object_textblock_text_markup_prepend(Evas_Textblock_Cursor *cur, const char
4953 } 5022 }
4954 } 5023 }
4955 /* Unicode object replcament char */ 5024 /* Unicode object replcament char */
4956 else if (!strncmp("\xEF\xBF\xBC", p, 3)) 5025 else if (!strncmp(_REPLACEMENT_CHAR_UTF8, p,
5026 text_len = strlen(_REPLACEMENT_CHAR_UTF8)) ||
5027 !strncmp(_NEWLINE_UTF8, p,
5028 text_len = strlen(_NEWLINE_UTF8)) ||
5029 !strncmp(_TAB_UTF8, p,
5030 text_len = strlen(_TAB_UTF8)) ||
5031 !strncmp(_PARAGRAPH_SEPARATOR_UTF8, p,
5032 text_len = strlen(_PARAGRAPH_SEPARATOR_UTF8)))
4957 { 5033 {
4958 /*FIXME: currently just remove them, maybe do something 5034 /*FIXME: currently just remove them, maybe do something
4959 * fancier in the future, atm it breaks if this char 5035 * fancier in the future, atm it breaks if this char
4960 * is inside <> */ 5036 * is inside <> */
4961 _prepend_text_run(cur, s, p); 5037 _prepend_text_run(cur, s, p);
4962 p += 2; /* it's also advanced later in this loop need +3 5038 /* it's also advanced later in this loop need +text_len
4963 * in total*/ 5039 in total*/
5040 p += text_len - 1;
4964 s = p + 1; /* One after the end of the replacement char */ 5041 s = p + 1; /* One after the end of the replacement char */
4965 } 5042 }
4966 p++; 5043 p++;
@@ -4979,19 +5056,19 @@ evas_object_textblock_text_markup_prepend(Evas_Textblock_Cursor *cur, const char
4979 * @param fnode the format node to process. 5056 * @param fnode the format node to process.
4980 */ 5057 */
4981static void 5058static void
4982_markup_get_format_append(Evas_Object_Textblock *o __UNUSED__, Eina_Strbuf *txt, Evas_Object_Textblock_Node_Format *fnode) 5059_markup_get_format_append(Eina_Strbuf *txt, Evas_Object_Textblock_Node_Format *fnode)
4983{ 5060{
4984 eina_strbuf_append_char(txt, '<'); 5061 eina_strbuf_append_char(txt, '<');
4985 { 5062 {
4986 const char *s; 5063 const char *s;
4987 int pop = 0;
4988 5064
4989 // FIXME: need to escape 5065 // FIXME: need to escape
4990 s = fnode->orig_format; 5066 s = fnode->orig_format;
4991 if (*s == '-') pop = 1; 5067 if (!fnode->opener && !fnode->own_closer)
4992 while ((*s == ' ') || (*s == '+') || (*s == '-')) s++; 5068 eina_strbuf_append_char(txt, '/');
4993 if (pop) eina_strbuf_append_char(txt, '/');
4994 eina_strbuf_append(txt, s); 5069 eina_strbuf_append(txt, s);
5070 if (fnode->own_closer)
5071 eina_strbuf_append_char(txt, '/');
4995 } 5072 }
4996 eina_strbuf_append_char(txt, '>'); 5073 eina_strbuf_append_char(txt, '>');
4997} 5074}
@@ -5061,7 +5138,7 @@ evas_object_textblock_text_markup_get(const Evas_Object *obj)
5061 tmp_ch = text[off]; 5138 tmp_ch = text[off];
5062 text[off] = 0; /* Null terminate the part of the string */ 5139 text[off] = 0; /* Null terminate the part of the string */
5063 _markup_get_text_append(txt, text); 5140 _markup_get_text_append(txt, text);
5064 _markup_get_format_append(o, txt, fnode); 5141 _markup_get_format_append(txt, fnode);
5065 text[off] = tmp_ch; /* Restore the char */ 5142 text[off] = tmp_ch; /* Restore the char */
5066 text += off; 5143 text += off;
5067 if (fnode->visible) 5144 if (fnode->visible)
@@ -5086,6 +5163,190 @@ evas_object_textblock_text_markup_get(const Evas_Object *obj)
5086 return o->markup_text; 5163 return o->markup_text;
5087} 5164}
5088 5165
5166EAPI char *
5167evas_textblock_text_markup_to_utf8(const Evas_Object *obj, const char *text)
5168{
5169 /* FIXME: Redundant and awful, should be merged with markup_prepend */
5170 Eina_Strbuf *sbuf;
5171 char *s, *p, *ret;
5172 char *tag_start, *tag_end, *esc_start, *esc_end;
5173
5174 if (!text) return NULL;
5175
5176
5177 tag_start = tag_end = esc_start = esc_end = NULL;
5178 sbuf = eina_strbuf_new();
5179 p = (char *)text;
5180 s = p;
5181 /* This loop goes through all of the mark up text until it finds format
5182 * tags, escape sequences or the terminating NULL. When it finds either
5183 * of those, it appends the text found up until that point to the textblock
5184 * proccesses whatever found. It repeats itself until the termainating
5185 * NULL is reached. */
5186 for (;;)
5187 {
5188 /* If we got to the end of string or just finished/started tag
5189 * or escape sequence handling. */
5190 if ((*p == 0) ||
5191 (tag_end) || (esc_end) ||
5192 (tag_start) || (esc_start))
5193 {
5194 if (tag_end)
5195 {
5196 /* If we reached to a tag ending, analyze the tag */
5197 char *ttag;
5198 size_t ttag_len;
5199
5200 tag_start++; /* Skip the < */
5201 tag_end--; /* Skip the > */
5202 if ((tag_end > tag_start) && (*(tag_end - 1) == '/'))
5203 {
5204 tag_end --; /* Skip the terminating '/' */
5205 while (*(tag_end - 1) == ' ')
5206 tag_end--; /* skip trailing ' ' */
5207 }
5208
5209 ttag_len = tag_end - tag_start;
5210
5211 ttag = malloc(ttag_len + 1);
5212 if (ttag)
5213 {
5214 const char *match = NULL;
5215 size_t replace_len;
5216 memcpy(ttag, tag_start, ttag_len);
5217 ttag[ttag_len] = 0;
5218
5219
5220 if (obj)
5221 {
5222 match = _style_match_tag(
5223 evas_object_textblock_style_get(obj),
5224 ttag, ttag_len, &replace_len);
5225 }
5226
5227 if (!match) match = ttag;
5228
5229 if (_IS_PARAGRAPH_SEPARATOR_SIMPLE(match))
5230 eina_strbuf_append(sbuf, _PARAGRAPH_SEPARATOR_UTF8);
5231 else if (_IS_LINE_SEPARATOR(match))
5232 eina_strbuf_append(sbuf, _NEWLINE_UTF8);
5233 else if (_IS_TAB(match))
5234 eina_strbuf_append(sbuf, _TAB_UTF8);
5235 else if (!strncmp(match, "item", 4))
5236 eina_strbuf_append(sbuf, _REPLACEMENT_CHAR_UTF8);
5237
5238 free(ttag);
5239 }
5240 tag_start = tag_end = NULL;
5241 }
5242 else if (esc_end)
5243 {
5244 const char *escape;
5245
5246 escape = _escaped_char_get(esc_start, esc_end + 1);
5247 eina_strbuf_append(sbuf, escape);
5248 esc_start = esc_end = NULL;
5249 }
5250 else if (*p == 0)
5251 {
5252 eina_strbuf_append_length(sbuf, s, p - s);
5253 s = NULL;
5254 }
5255 if (*p == 0)
5256 break;
5257 }
5258 if (*p == '<')
5259 {
5260 if (!esc_start)
5261 {
5262 /* Append the text prior to this to the textblock and
5263 * mark the start of the tag */
5264 tag_start = p;
5265 tag_end = NULL;
5266 eina_strbuf_append_length(sbuf, s, p - s);
5267 s = NULL;
5268 }
5269 }
5270 else if (*p == '>')
5271 {
5272 if (tag_start)
5273 {
5274 tag_end = p + 1;
5275 s = p + 1;
5276 }
5277 }
5278 else if (*p == '&')
5279 {
5280 if (!tag_start)
5281 {
5282 /* Append the text prior to this to the textblock and mark
5283 * the start of the escape sequence */
5284 esc_start = p;
5285 esc_end = NULL;
5286 eina_strbuf_append_length(sbuf, s, p - s);
5287 s = NULL;
5288 }
5289 }
5290 else if (*p == ';')
5291 {
5292 if (esc_start)
5293 {
5294 esc_end = p;
5295 s = p + 1;
5296 }
5297 }
5298 p++;
5299 }
5300
5301 ret = eina_strbuf_string_steal(sbuf);
5302 eina_strbuf_free(sbuf);
5303 return ret;
5304}
5305
5306EAPI char *
5307evas_textblock_text_utf8_to_markup(const Evas_Object *obj, const char *text)
5308{
5309 Eina_Strbuf *sbuf;
5310 char *str = NULL;
5311 int ch, pos = 0, pos2 = 0;
5312
5313 (void) obj;
5314
5315 if (!text) return NULL;
5316
5317 sbuf = eina_strbuf_new();
5318
5319 for (;;)
5320 {
5321 pos = pos2;
5322 pos2 = evas_string_char_next_get(text, pos2, &ch);
5323 if ((ch <= 0) || (pos2 <= 0)) break;
5324
5325 if (ch == _NEWLINE)
5326 eina_strbuf_append(sbuf, "<br/>");
5327 else if (ch == _TAB)
5328 eina_strbuf_append(sbuf, "<tab/>");
5329 else if (ch == '<')
5330 eina_strbuf_append(sbuf, "&lt;");
5331 else if (ch == '>')
5332 eina_strbuf_append(sbuf, "&gt;");
5333 else if (ch == '&')
5334 eina_strbuf_append(sbuf, "&amp;");
5335 else if (ch == _PARAGRAPH_SEPARATOR)
5336 eina_strbuf_append(sbuf, "<ps/>");
5337 else if (ch == _REPLACEMENT_CHAR)
5338 eina_strbuf_append(sbuf, "&#xfffc;");
5339 else
5340 {
5341 eina_strbuf_append_length(sbuf, text + pos, pos2 - pos);
5342 }
5343 }
5344 str = eina_strbuf_string_steal(sbuf);
5345 eina_strbuf_free(sbuf);
5346 return str;
5347
5348}
5349
5089/* cursors */ 5350/* cursors */
5090 5351
5091/** 5352/**
@@ -5453,15 +5714,13 @@ evas_textblock_node_format_remove_pair(Evas_Object *obj,
5453 { 5714 {
5454 const char *fstr = fmt->orig_format; 5715 const char *fstr = fmt->orig_format;
5455 5716
5456 if (fstr && (*fstr == '+')) 5717 if (fmt->opener && !fmt->own_closer)
5457 { 5718 {
5458 fstack = eina_list_prepend(fstack, fmt); 5719 fstack = eina_list_prepend(fstack, fmt);
5459 } 5720 }
5460 else if (fstr && (*fstr == '-')) 5721 else if (fstr && !fmt->opener)
5461 { 5722 {
5462 size_t fstr_len; 5723 size_t fstr_len;
5463 /* Skip the '-' */
5464 fstr++;
5465 fstr_len = strlen(fstr); 5724 fstr_len = strlen(fstr);
5466 /* Generic popper, just pop */ 5725 /* Generic popper, just pop */
5467 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0]) 5726 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0])
@@ -5708,6 +5967,111 @@ evas_textblock_cursor_format_prev(Evas_Textblock_Cursor *cur)
5708 return EINA_FALSE; 5967 return EINA_FALSE;
5709} 5968}
5710 5969
5970#ifdef HAVE_LINEBREAK
5971
5972/* BREAK_AFTER: true if we can break after the current char.
5973 * Both macros assume str[i] is not the terminating nul */
5974#define BREAK_AFTER(i) \
5975 (breaks[i] == WORDBREAK_BREAK)
5976
5977#else
5978
5979#define BREAK_AFTER(i) \
5980 ((!str[i + 1]) || \
5981 (_is_white(str[i]) && !_is_white(str[i + 1])) || \
5982 (!_is_white(str[i]) && _is_white(str[i + 1])))
5983
5984#endif
5985
5986EAPI Eina_Bool
5987evas_textblock_cursor_word_start(Evas_Textblock_Cursor *cur)
5988{
5989 const Eina_Unicode *text;
5990 size_t i;
5991#ifdef HAVE_LINEBREAK
5992 char *breaks;
5993#endif
5994
5995 if (!cur) return EINA_FALSE;
5996 if (!cur->node) return EINA_FALSE;
5997
5998 text = eina_ustrbuf_string_get(cur->node->unicode);
5999
6000#ifdef HAVE_LINEBREAK
6001 {
6002 const char *lang = ""; /* FIXME: get lang */
6003 size_t len = eina_ustrbuf_length_get(cur->node->unicode);
6004 breaks = malloc(len);
6005 set_wordbreaks_utf32((const utf32_t *) text, len, lang, breaks);
6006 }
6007#endif
6008
6009 i = cur->pos;
6010
6011 /* Skip the first one. This ensures we don't point to the nul, and also
6012 * we just don't care about it anyway. */
6013 if (i > 0) i--;
6014
6015 for ( ; i > 0 ; i--)
6016 {
6017 if (BREAK_AFTER(i))
6018 {
6019 /* Advance to the current char */
6020 i++;
6021 break;
6022 }
6023 }
6024
6025 cur->pos = i;
6026
6027#ifdef HAVE_LINEBREAK
6028 free(breaks);
6029#endif
6030 return EINA_TRUE;
6031}
6032
6033EAPI Eina_Bool
6034evas_textblock_cursor_word_end(Evas_Textblock_Cursor *cur)
6035{
6036 const Eina_Unicode *text;
6037 size_t i;
6038#ifdef HAVE_LINEBREAK
6039 char *breaks;
6040#endif
6041
6042 if (!cur) return EINA_FALSE;
6043 if (!cur->node) return EINA_FALSE;
6044
6045 text = eina_ustrbuf_string_get(cur->node->unicode);
6046
6047#ifdef HAVE_LINEBREAK
6048 {
6049 const char *lang = ""; /* FIXME: get lang */
6050 size_t len = eina_ustrbuf_length_get(cur->node->unicode);
6051 breaks = malloc(len);
6052 set_wordbreaks_utf32((const utf32_t *) text, len, lang, breaks);
6053 }
6054#endif
6055
6056 i = cur->pos;
6057
6058 for ( ; text[i] ; i++)
6059 {
6060 if (BREAK_AFTER(i))
6061 {
6062 /* This is the one to break after. */
6063 break;
6064 }
6065 }
6066
6067 cur->pos = i;
6068
6069#ifdef HAVE_LINEBREAK
6070 free(breaks);
6071#endif
6072 return EINA_TRUE;;
6073}
6074
5711EAPI Eina_Bool 6075EAPI Eina_Bool
5712evas_textblock_cursor_char_next(Evas_Textblock_Cursor *cur) 6076evas_textblock_cursor_char_next(Evas_Textblock_Cursor *cur)
5713{ 6077{
@@ -5887,10 +6251,9 @@ _evas_textblock_format_is_visible(Evas_Object_Textblock_Node_Format *fnode,
5887 fnode->anchor = ANCHOR_NONE; 6251 fnode->anchor = ANCHOR_NONE;
5888 if (!s) return; 6252 if (!s) return;
5889 6253
5890 if (s[0] == '+' || s[0] == '-') 6254 if (!fnode->own_closer)
5891 { 6255 {
5892 is_opener = (s[0] == '+'); 6256 is_opener = fnode->opener;
5893 s++;
5894 fnode->format_change = EINA_TRUE; 6257 fnode->format_change = EINA_TRUE;
5895 } 6258 }
5896 6259
@@ -5902,6 +6265,8 @@ _evas_textblock_format_is_visible(Evas_Object_Textblock_Node_Format *fnode,
5902 * closing */ 6265 * closing */
5903 if ((!strncmp(item, "\n", itlen) || !strncmp(item, "\\n", itlen)) || 6266 if ((!strncmp(item, "\n", itlen) || !strncmp(item, "\\n", itlen)) ||
5904 (!strncmp(item, "\t", itlen) || !strncmp(item, "\\t", itlen)) || 6267 (!strncmp(item, "\t", itlen) || !strncmp(item, "\\t", itlen)) ||
6268 (!strncmp(item, "br", itlen) && (itlen >= 2)) ||
6269 (!strncmp(item, "tab", itlen) && (itlen >= 3)) ||
5905 (!strncmp(item, "ps", itlen) && (itlen >= 2)) || 6270 (!strncmp(item, "ps", itlen) && (itlen >= 2)) ||
5906 (!strncmp(item, "item", itlen) && (itlen >= 4) && is_opener)) 6271 (!strncmp(item, "item", itlen) && (itlen >= 4) && is_opener))
5907 { 6272 {
@@ -5986,19 +6351,17 @@ _evas_textblock_node_format_remove_matching(Evas_Object_Textblock *o,
5986 } 6351 }
5987 6352
5988 6353
5989 if (fstr && (*fstr == '+')) 6354 if (fmt->opener && !fmt->own_closer)
5990 { 6355 {
5991 formats = eina_list_prepend(formats, fmt); 6356 formats = eina_list_prepend(formats, fmt);
5992 } 6357 }
5993 else if (fstr && (*fstr == '-')) 6358 else if (fstr && !fmt->opener)
5994 { 6359 {
5995 Evas_Object_Textblock_Node_Format *fnode; 6360 Evas_Object_Textblock_Node_Format *fnode;
5996 size_t fstr_len; 6361 size_t fstr_len;
5997 /* Skip the '-' */
5998 fstr++;
5999 fstr_len = strlen(fstr); 6362 fstr_len = strlen(fstr);
6000 /* Generic popper, just pop */ 6363 /* Generic popper, just pop (if there's anything to pop). */
6001 if (((fstr[0] == ' ') && !fstr[1]) || !fstr[0]) 6364 if (formats && (((fstr[0] == ' ') && !fstr[1]) || !fstr[0]))
6002 { 6365 {
6003 fnode = eina_list_data_get(formats); 6366 fnode = eina_list_data_get(formats);
6004 formats = eina_list_remove_list(formats, formats); 6367 formats = eina_list_remove_list(formats, formats);
@@ -6855,6 +7218,7 @@ _evas_textblock_node_format_new(Evas_Object_Textblock *o, const char *_format)
6855{ 7218{
6856 Evas_Object_Textblock_Node_Format *n; 7219 Evas_Object_Textblock_Node_Format *n;
6857 const char *format = _format; 7220 const char *format = _format;
7221 const char *pre_stripped_format = NULL;
6858 7222
6859 n = calloc(1, sizeof(Evas_Object_Textblock_Node_Format)); 7223 n = calloc(1, sizeof(Evas_Object_Textblock_Node_Format));
6860 /* Create orig_format and format */ 7224 /* Create orig_format and format */
@@ -6866,62 +7230,80 @@ _evas_textblock_node_format_new(Evas_Object_Textblock *o, const char *_format)
6866 7230
6867 format++; /* Advance after '<' */ 7231 format++; /* Advance after '<' */
6868 format_len = strlen(format); 7232 format_len = strlen(format);
6869 if (format[format_len - 1] == '>') 7233 if ((format_len > 0) && format[format_len - 1] == '>')
6870 format_len--; /* We don't care about '>' */ 7234 {
7235 format_len--; /* We don't care about '>' */
7236 /* Check if it closes itself. Skip the </> case. */
7237 if ((format_len > 1) && format[format_len - 1] == '/')
7238 {
7239 format_len--; /* We don't care about '/' */
7240 n->own_closer = EINA_TRUE;
7241 }
7242 }
6871 7243
6872 match = _style_match_tag(o->style, format, format_len, &replace_len); 7244 match = _style_match_tag(o->style, format, format_len, &replace_len);
6873 if (match) 7245 if (match)
6874 { 7246 {
6875 if ((match[0] == '+') || (match[0] == '-')) 7247 if (match[0] != '-')
6876 {
6877 char *norm_format;
6878 norm_format = malloc(format_len + 2 + 1);
6879 memcpy(norm_format, match, 2);
6880 memcpy(norm_format + 2, format, format_len);
6881 norm_format[format_len + 2] = '\0';
6882 n->orig_format =
6883 eina_stringshare_add_length(norm_format, format_len + 2);
6884 free(norm_format);
6885 }
6886 else
6887 { 7248 {
6888 n->orig_format = 7249 n->opener = EINA_TRUE;
6889 eina_stringshare_add_length(format, format_len); 7250 if (match[0] != '+')
7251 {
7252 n->own_closer = EINA_TRUE;
7253 }
6890 } 7254 }
6891 n->format = eina_stringshare_add(match); 7255
7256 pre_stripped_format = match;
6892 } 7257 }
6893 else 7258 else
6894 { 7259 {
6895 char *norm_format; 7260 if (format[0] == '/')
6896
6897 norm_format = malloc(format_len + 2 + 1);
6898 if (norm_format)
6899 { 7261 {
6900 if (format[0] == '/') 7262 format++;
6901 { 7263 format_len--;
6902 memcpy(norm_format, "- ", 2); 7264 }
6903 memcpy(norm_format + 2, format + 1, format_len - 1); 7265 else
6904 norm_format[format_len + 2 - 1] = '\0'; 7266 {
6905 } 7267 n->opener = EINA_TRUE;
6906 else
6907 {
6908 memcpy(norm_format, "+ ", 2);
6909 memcpy(norm_format + 2, format, format_len);
6910 norm_format[format_len + 2] = '\0';
6911 }
6912 n->orig_format = eina_stringshare_add(norm_format);
6913 free(norm_format);
6914 } 7268 }
6915 n->format = eina_stringshare_ref(n->orig_format);
6916 } 7269 }
7270
7271 n->orig_format = eina_stringshare_add_length(format, format_len);
7272
7273 if (!pre_stripped_format)
7274 pre_stripped_format = n->orig_format;
6917 } 7275 }
6918 /* Just use as is, it's a special format. */ 7276 /* Just use as is, it's a special format. */
6919 else 7277 else
6920 { 7278 {
6921 n->orig_format = eina_stringshare_add(format); 7279 const char *tmp = format;
6922 n->format = eina_stringshare_ref(n->orig_format); 7280 if (format[0] != '-')
7281 {
7282 n->opener = EINA_TRUE;
7283 if (format[0] != '+')
7284 {
7285 n->own_closer = EINA_TRUE;
7286 }
7287 }
7288 if ((*tmp == '+') || (*tmp == '-'))
7289 {
7290 tmp++;
7291 while (*tmp == ' ') tmp++;
7292 }
7293 n->orig_format = eina_stringshare_add(tmp);
7294 pre_stripped_format = n->orig_format;
6923 } 7295 }
6924 7296
7297 /* Strip format */
7298 {
7299 const char *tmp = pre_stripped_format;
7300 if ((*tmp == '+') || (*tmp == '-'))
7301 {
7302 tmp++;
7303 while (*tmp == ' ') tmp++;
7304 }
7305 n->format = eina_stringshare_add(tmp);
7306 }
6925 format = n->format; 7307 format = n->format;
6926 7308
6927 _evas_textblock_format_is_visible(n, format); 7309 _evas_textblock_format_is_visible(n, format);
@@ -7051,11 +7433,11 @@ evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, const char *form
7051 if (_IS_PARAGRAPH_SEPARATOR(o, format)) 7433 if (_IS_PARAGRAPH_SEPARATOR(o, format))
7052 insert_char = _PARAGRAPH_SEPARATOR; 7434 insert_char = _PARAGRAPH_SEPARATOR;
7053 else if (_IS_LINE_SEPARATOR(format)) 7435 else if (_IS_LINE_SEPARATOR(format))
7054 insert_char = '\n'; 7436 insert_char = _NEWLINE;
7055 else if (_IS_TAB(format)) 7437 else if (_IS_TAB(format))
7056 insert_char = '\t'; 7438 insert_char = _TAB;
7057 else 7439 else
7058 insert_char = EVAS_TEXTBLOCK_REPLACEMENT_CHAR; 7440 insert_char = _REPLACEMENT_CHAR;
7059 7441
7060 eina_ustrbuf_insert_char(cur->node->unicode, insert_char, cur->pos); 7442 eina_ustrbuf_insert_char(cur->node->unicode, insert_char, cur->pos);
7061 7443
@@ -7142,7 +7524,7 @@ evas_textblock_cursor_char_delete(Evas_Textblock_Cursor *cur)
7142 should_merge = EINA_TRUE; 7524 should_merge = EINA_TRUE;
7143 } 7525 }
7144 /* If a singnular, mark as invisible, so we'll delete it. */ 7526 /* If a singnular, mark as invisible, so we'll delete it. */
7145 if (!format || ((*format != '+') && (*format != '-'))) 7527 if (!format || last_fmt->own_closer)
7146 { 7528 {
7147 last_fmt->visible = EINA_FALSE; 7529 last_fmt->visible = EINA_FALSE;
7148 } 7530 }
@@ -7271,60 +7653,40 @@ evas_textblock_cursor_range_delete(Evas_Textblock_Cursor *cur1, Evas_Textblock_C
7271EAPI char * 7653EAPI char *
7272evas_textblock_cursor_content_get(const Evas_Textblock_Cursor *cur) 7654evas_textblock_cursor_content_get(const Evas_Textblock_Cursor *cur)
7273{ 7655{
7274 const Eina_Unicode *ustr;
7275 Eina_Unicode buf[2];
7276 char *s;
7277 if (!cur || !cur->node) return NULL; 7656 if (!cur || !cur->node) return NULL;
7278 if (evas_textblock_cursor_format_is_visible_get(cur)) 7657 if (evas_textblock_cursor_format_is_visible_get(cur))
7279 { 7658 {
7280 size_t len; 7659 Eina_Strbuf *buf;
7281 const char *fstr; 7660 Evas_Object_Textblock_Node_Format *fnode;
7282 char *ret; 7661 char *ret;
7283 int pop = 0; 7662 fnode = _evas_textblock_node_visible_at_pos_get(
7284 fstr = evas_textblock_node_format_text_get( 7663 evas_textblock_cursor_format_get(cur));
7285 _evas_textblock_node_visible_at_pos_get(
7286 evas_textblock_cursor_format_get(cur)));
7287
7288 if (!fstr)
7289 return NULL;
7290 7664
7291 if (*fstr == '-') pop = 1; 7665 buf = eina_strbuf_new();
7292 while ((*fstr == ' ') || (*fstr == '+') || (*fstr == '-')) fstr++; 7666 _markup_get_format_append(buf, fnode);
7293 len = strlen(fstr); 7667 ret = eina_strbuf_string_steal(buf);
7294 7668 eina_strbuf_free(buf);
7295 {
7296 char *tmp;
7297 if (pop)
7298 {
7299 ret = tmp = malloc(len + 3 + 1); /* </> and the null */
7300 memcpy(tmp, "</", 2);
7301 tmp += 2;
7302 }
7303 else
7304 {
7305 ret = tmp = malloc(len + 2 + 1); /* <> and the null */
7306 *tmp = '<';
7307 tmp++;
7308 }
7309 memcpy(tmp, fstr, len);
7310 memcpy(tmp + len, ">", 2); /* Including the null */
7311 }
7312 7669
7313 return ret; 7670 return ret;
7314 } 7671 }
7672 else
7673 {
7674 const Eina_Unicode *ustr;
7675 Eina_Unicode buf[2];
7676 char *s;
7315 7677
7316 ustr = eina_ustrbuf_string_get(cur->node->unicode); 7678 ustr = eina_ustrbuf_string_get(cur->node->unicode);
7317 buf[0] = ustr[cur->pos]; 7679 buf[0] = ustr[cur->pos];
7318 buf[1] = 0; 7680 buf[1] = 0;
7319 s = eina_unicode_unicode_to_utf8(buf, NULL); 7681 s = eina_unicode_unicode_to_utf8(buf, NULL);
7320 7682
7321 return s; 7683 return s;
7684 }
7322} 7685}
7323 7686
7324static char * 7687static char *
7325_evas_textblock_cursor_range_text_markup_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2) 7688_evas_textblock_cursor_range_text_markup_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *_cur2)
7326{ 7689{
7327 Evas_Object_Textblock *o;
7328 Evas_Object_Textblock_Node_Text *tnode; 7690 Evas_Object_Textblock_Node_Text *tnode;
7329 Eina_Strbuf *buf; 7691 Eina_Strbuf *buf;
7330 Evas_Textblock_Cursor *cur2; 7692 Evas_Textblock_Cursor *cur2;
@@ -7333,7 +7695,6 @@ _evas_textblock_cursor_range_text_markup_get(const Evas_Textblock_Cursor *cur1,
7333 if (!cur1 || !cur1->node) return NULL; 7695 if (!cur1 || !cur1->node) return NULL;
7334 if (!_cur2 || !_cur2->node) return NULL; 7696 if (!_cur2 || !_cur2->node) return NULL;
7335 if (cur1->obj != _cur2->obj) return NULL; 7697 if (cur1->obj != _cur2->obj) return NULL;
7336 o = (Evas_Object_Textblock *)(cur1->obj->object_data);
7337 if (evas_textblock_cursor_compare(cur1, _cur2) > 0) 7698 if (evas_textblock_cursor_compare(cur1, _cur2) > 0)
7338 { 7699 {
7339 const Evas_Textblock_Cursor *tc; 7700 const Evas_Textblock_Cursor *tc;
@@ -7401,7 +7762,7 @@ _evas_textblock_cursor_range_text_markup_get(const Evas_Textblock_Cursor *cur1,
7401 tmp_ch = text[off]; 7762 tmp_ch = text[off];
7402 text[off] = 0; /* Null terminate the part of the string */ 7763 text[off] = 0; /* Null terminate the part of the string */
7403 _markup_get_text_append(buf, text); 7764 _markup_get_text_append(buf, text);
7404 _markup_get_format_append(o, buf, fnode); 7765 _markup_get_format_append(buf, fnode);
7405 text[off] = tmp_ch; /* Restore the char */ 7766 text[off] = tmp_ch; /* Restore the char */
7406 text += off; 7767 text += off;
7407 if (fnode->visible) 7768 if (fnode->visible)
@@ -7623,8 +7984,27 @@ evas_textblock_cursor_format_get(const Evas_Textblock_Cursor *cur)
7623EAPI const char * 7984EAPI const char *
7624evas_textblock_node_format_text_get(const Evas_Object_Textblock_Node_Format *fmt) 7985evas_textblock_node_format_text_get(const Evas_Object_Textblock_Node_Format *fmt)
7625{ 7986{
7987 static char *ret = NULL;
7988 char *tmp;
7989
7626 if (!fmt) return NULL; 7990 if (!fmt) return NULL;
7627 return fmt->orig_format; 7991
7992 if (ret) free(ret);
7993 ret = malloc(strlen(fmt->orig_format) + 2 + 1);
7994 tmp = ret;
7995
7996 if (fmt->opener && !fmt->own_closer)
7997 {
7998 *(tmp++) = '+';
7999 *(tmp++) = ' ';
8000 }
8001 else if (!fmt->opener)
8002 {
8003 *(tmp++) = '-';
8004 *(tmp++) = ' ';
8005 }
8006 strcpy(tmp, fmt->orig_format);
8007 return ret;
7628} 8008}
7629 8009
7630EAPI void 8010EAPI void
@@ -7673,7 +8053,9 @@ evas_textblock_cursor_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord
7673 * the position of the previous */ 8053 * the position of the previous */
7674 if ((cur->pos > 0) && !_evas_textblock_cursor_is_at_the_end(cur)) 8054 if ((cur->pos > 0) && !_evas_textblock_cursor_is_at_the_end(cur))
7675 { 8055 {
8056#ifdef BIDI_SUPPORT
7676 Eina_Bool before_char = EINA_FALSE; 8057 Eina_Bool before_char = EINA_FALSE;
8058#endif
7677 cur2.obj = cur->obj; 8059 cur2.obj = cur->obj;
7678 evas_textblock_cursor_copy(cur, &cur2); 8060 evas_textblock_cursor_copy(cur, &cur2);
7679 evas_textblock_cursor_char_prev(&cur2); 8061 evas_textblock_cursor_char_prev(&cur2);
@@ -7683,12 +8065,16 @@ evas_textblock_cursor_geometry_get(const Evas_Textblock_Cursor *cur, Evas_Coord
7683 if (!fmt || !_IS_LINE_SEPARATOR(fmt->format)) 8065 if (!fmt || !_IS_LINE_SEPARATOR(fmt->format))
7684 { 8066 {
7685 dir_cur = &cur2; 8067 dir_cur = &cur2;
8068#ifdef BIDI_SUPPORT
7686 before_char = EINA_FALSE; 8069 before_char = EINA_FALSE;
8070#endif
7687 } 8071 }
8072#ifdef BIDI_SUPPORT
7688 else 8073 else
7689 { 8074 {
7690 before_char = EINA_TRUE; 8075 before_char = EINA_TRUE;
7691 } 8076 }
8077#endif
7692 ret = evas_textblock_cursor_pen_geometry_get( 8078 ret = evas_textblock_cursor_pen_geometry_get(
7693 dir_cur, &x, &y, &w, &h); 8079 dir_cur, &x, &y, &w, &h);
7694#ifdef BIDI_SUPPORT 8080#ifdef BIDI_SUPPORT
@@ -8788,6 +9174,7 @@ evas_object_textblock_init(Evas_Object *obj)
8788 { 9174 {
8789 linebreak_init = EINA_TRUE; 9175 linebreak_init = EINA_TRUE;
8790 init_linebreak(); 9176 init_linebreak();
9177 init_wordbreak();
8791 } 9178 }
8792#endif 9179#endif
8793 9180