Evas textblock: Don't keep a copy of the string per text item.
authortasn <tasn@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Wed, 13 Apr 2011 08:36:57 +0000 (08:36 +0000)
committertasn <tasn@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Wed, 13 Apr 2011 08:36:57 +0000 (08:36 +0000)
We don't need to copy it around because we have string objects now. This
lets us remove a lot of unneeded allocations and unwanted strcpys.

git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/evas@58619 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

src/lib/canvas/evas_object_textblock.c

index 5c5cd36..aa02d26 100644 (file)
@@ -137,7 +137,13 @@ typedef struct _Evas_Object_Textblock_Format_Item Evas_Object_Textblock_Format_I
  */
 typedef struct _Evas_Object_Textblock_Format      Evas_Object_Textblock_Format;
 
-/* the current state of the formatting */
+/**
+ * @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 GET_PREV(text, ind)
@@ -145,7 +151,8 @@ typedef struct _Evas_Object_Textblock_Format      Evas_Object_Textblock_Format;
  * the current char pointed to and decrements ind but ensures it stays in
  * the text range.
  */
-#define GET_PREV(text, ind) (text ? (((ind) > 0) ? (text[(ind)--]) : (text[ind])) : 0)
+#define GET_PREV(text, ind) ((text) ? (((ind) > 0) ? ((text)[(ind)--]) : \
+         ((text)[ind])) : 0)
 /**
  * @internal
  * @def GET_NEXT(text, ind)
@@ -153,7 +160,18 @@ typedef struct _Evas_Object_Textblock_Format      Evas_Object_Textblock_Format;
  * the current char pointed to and increments indd but ensures it stays in
  * the text range.
  */
-#define GET_NEXT(text, ind) (text ? ((text[ind]) ? (text[(ind)++]) : (text[ind])) : 0)
+#define GET_NEXT(text, ti, ind) (((text) && !IS_AT_END(ti, (size_t) ind)) ? \
+   ((text)[(ind)++]) : 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)
 
 /*FIXME: document the structs and struct items. */
 struct _Evas_Object_Style_Tag
@@ -267,7 +285,6 @@ struct _Evas_Object_Textblock_Item
 struct _Evas_Object_Textblock_Text_Item
 {
    Evas_Object_Textblock_Item       parent;
-   Eina_Unicode                    *text;
    Evas_Text_Props                  text_props;
    int                              inset, baseline;
    Evas_Coord                       x_adjustment; /* Used to indicate by how
@@ -608,7 +625,6 @@ _item_free(const Evas_Object *obj, Evas_Object_Textblock_Line *ln, Evas_Object_T
         Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
 
         evas_common_text_props_content_unref(&ti->text_props);
-        if (ti->text) free(ti->text);
      }
    else
      {
@@ -2316,14 +2332,13 @@ _layout_line_advance(Ctxt *c, Evas_Object_Textblock_Format *fmt)
  * @param len the length of the string.
  */
 static Evas_Object_Textblock_Text_Item *
-_layout_text_item_new(Ctxt *c __UNUSED__, Evas_Object_Textblock_Format *fmt, const Eina_Unicode *str, size_t len)
+_layout_text_item_new(Ctxt *c __UNUSED__, Evas_Object_Textblock_Format *fmt)
 {
    Evas_Object_Textblock_Text_Item *ti;
 
    ti = calloc(1, sizeof(Evas_Object_Textblock_Text_Item));
    ti->parent.format = fmt;
    ti->parent.format->ref++;
-   ti->text = eina_unicode_strndup(str, len);
    ti->parent.type = EVAS_TEXTBLOCK_ITEM_TEXT;
    return ti;
 }
@@ -2369,24 +2384,22 @@ static Evas_Object_Textblock_Text_Item *
 _layout_item_text_split_strip_white(Ctxt *c,
       Evas_Object_Textblock_Text_Item *ti, Eina_List *lti, int cut)
 {
-   Eina_Unicode *ts;
+   const Eina_Unicode *ts;
    Evas_Object_Textblock_Text_Item *new_ti = NULL, *white_ti = NULL;
    int cut2;
 
-   ts = ti->text;
+   ts = GET_ITEM_TEXT(ti);
    if (_is_white(ts[cut]))
      cut2 = cut + 1;
    else
      cut2 = cut;
 
-   if (ts[cut2] && (ti->text_props.text_len > 0))
+   if (!IS_AT_END(ti, (size_t) cut2) && (ti->text_props.text_len > 0))
      {
-        new_ti = _layout_text_item_new(c, ti->parent.format, &ts[cut2],
-                                       ti->text_props.text_len - cut2);
+        new_ti = _layout_text_item_new(c, ti->parent.format);
         new_ti->parent.text_node = ti->parent.text_node;
         new_ti->parent.text_pos = ti->parent.text_pos + cut2;
         new_ti->parent.merge = EINA_TRUE;
-        ts[cut2] = 0;
 
         evas_common_text_props_split(&ti->text_props,
                                      &new_ti->text_props, cut2);
@@ -2395,13 +2408,11 @@ _layout_item_text_split_strip_white(Ctxt *c,
 
    if ((cut2 > cut) && (ti->text_props.text_len > 0))
      {
-        white_ti = _layout_text_item_new(c, ti->parent.format, &ts[cut],
-                                       ti->text_props.text_len - cut);
+        white_ti = _layout_text_item_new(c, ti->parent.format);
         white_ti->parent.text_node = ti->parent.text_node;
         white_ti->parent.text_pos = ti->parent.text_pos + cut;
         white_ti->parent.merge = EINA_TRUE;
         white_ti->parent.visually_deleted = EINA_TRUE;
-        ts[cut] = 0;
 
         evas_common_text_props_split(&ti->text_props,
               &white_ti->text_props, cut);
@@ -2411,9 +2422,6 @@ _layout_item_text_split_strip_white(Ctxt *c,
    if (new_ti || white_ti)
      {
         _text_item_update_sizes(c, ti);
-
-        ti->text = eina_unicode_strndup(ts, cut);
-        free(ts);
      }
    return new_ti;
 }
@@ -2431,21 +2439,12 @@ _layout_item_merge_and_free(Ctxt *c,
       Evas_Object_Textblock_Text_Item *item1,
       Evas_Object_Textblock_Text_Item *item2)
 {
-   Eina_Unicode *tmp;
-   size_t len1, len2;
-   len1 = item1->text_props.text_len;
-   len2 = item2->text_props.text_len;
    evas_common_text_props_merge(&item1->text_props,
          &item2->text_props);
 
    item1->parent.w = item1->parent.adv + item2->parent.w;
    item1->parent.adv += item2->parent.adv;
 
-   tmp = realloc(item1->text, (len1 + len2 + 1) * sizeof(Eina_Unicode));
-   eina_unicode_strncpy(tmp + len1, item2->text, len2);
-   item1->text = tmp;
-   item1->text[len1 + len2] = 0;
-
    item1->parent.merge = EINA_FALSE;
    item1->parent.visually_deleted = EINA_FALSE;
 
@@ -2456,24 +2455,26 @@ _layout_item_merge_and_free(Ctxt *c,
  * @internal
  * Return the start of the last word up until start.
  *
- * @param str the string to work on.
+ * @param ti the relevant text item
  * @param start the start of where to look at.
  * @return the start of the last word up until start.
  */
 static int
-_layout_word_start(const Eina_Unicode *str, int start)
+_layout_word_start(const Evas_Object_Textblock_Text_Item *ti, int start)
 {
-   int p, tp, chr = 0;
+   size_t p, tp;
+   Eina_Unicode chr = 0;
+   const Eina_Unicode *str = GET_ITEM_TEXT(ti);
 
    p = start;
-   chr = GET_NEXT(str, p);
+   chr = GET_NEXT(str, ti, p);
    if (_is_white(chr))
      {
         tp = p;
-        while (_is_white(chr) && (p >= 0))
+        while (_is_white(chr))
           {
              tp = p;
-             chr = GET_NEXT(str, p);
+             chr = GET_NEXT(str, ti, p);
           }
         return tp;
      }
@@ -2485,10 +2486,9 @@ _layout_word_start(const Eina_Unicode *str, int start)
         if (_is_white(chr)) break;
         tp = p;
      }
-   if (p < 0) p = 0;
-   if ((p >= 0) && (_is_white(chr)))
+   if (_is_white(chr))
      {
-        GET_NEXT(str, p);
+        GET_NEXT(str, ti, p);
      }
    return p;
 }
@@ -2497,22 +2497,24 @@ _layout_word_start(const Eina_Unicode *str, int start)
  * @internal
  * returns the index of the words end starting from p
  *
- * @param str the str to work on - NOT NULL.
+ * @param ti the relevant text item
  * @param p start position - must be within strings range..
  *
  * @return the position of the end of the word. -1 on error.
  */
 static int
-_layout_word_end(const Eina_Unicode *str, int p)
+_layout_word_end(const Evas_Object_Textblock_Text_Item *ti, int p)
 {
-   int ch, tp;
+   size_t tp;
+   Eina_Unicode ch;
+   const Eina_Unicode *str = GET_ITEM_TEXT(ti);
 
    tp = p;
-   ch = GET_NEXT(str, tp);
-   while ((!_is_white(ch)) && (tp >= 0) && (ch != 0))
+   ch = GET_NEXT(str, ti, tp);
+   while ((!_is_white(ch)) && (ch != 0))
      {
         p = tp;
-        ch = GET_NEXT(str, tp);
+        ch = GET_NEXT(str, ti, tp);
      }
    if (ch == 0) return -1;
    return p;
@@ -2522,28 +2524,30 @@ _layout_word_end(const Eina_Unicode *str, int p)
  * @internal
  * returns the index of the start of the next word.
  *
- * @param str the str to work on - NOT NULL.
+ * @param ti the relevant text item
  * @param p start position - must be within strings range..
  *
  * @return the position of the start of the next word. -1 on error.
  */
 static int
-_layout_word_next(Eina_Unicode *str, int p)
+_layout_word_next(const Evas_Object_Textblock_Text_Item *ti, int p)
 {
-   int ch, tp;
+   size_t tp;
+   Eina_Unicode ch;
+   const Eina_Unicode *str = GET_ITEM_TEXT(ti);
 
    tp = p;
-   ch = GET_NEXT(str, tp);
-   while ((!_is_white(ch)) && (tp >= 0) && (ch != 0))
+   ch = GET_NEXT(str, ti, tp);
+   while ((!_is_white(ch)) && (ch != 0))
      {
         p = tp;
-        ch = GET_NEXT(str, tp);
+        ch = GET_NEXT(str, ti, tp);
      }
    if (ch == 0) return -1;
-   while ((_is_white(ch)) && (tp >= 0) && (ch != 0))
+   while ((_is_white(ch)) && (ch != 0))
      {
         p = tp;
-        ch = GET_NEXT(str, tp);
+        ch = GET_NEXT(str, ti, tp);
      }
    if (ch == 0) return -1;
    return p;
@@ -2755,7 +2759,7 @@ skip:
      {
         int tmp_len;
 
-        ti = _layout_text_item_new(c, fmt, str, cur_len);
+        ti = _layout_text_item_new(c, fmt);
         ti->parent.text_node = n;
         ti->parent.text_pos = start + str - tbase;
         tmp_len = off - (str - tbase);
@@ -2763,28 +2767,22 @@ skip:
           {
              int tmp_cut;
              tmp_cut = evas_common_language_script_end_of_run_get(
-                   ti->text,
+                   GET_ITEM_TEXT(ti),
                    ti->parent.text_node->bidi_props,
                    ti->parent.text_pos, tmp_len);
              if (tmp_cut > 0)
                {
-                  Eina_Unicode *ts;
-
-                  ts = ti->text;
-                  ts[tmp_cut] = 0;
-                  ti->text = eina_unicode_strndup(ts, tmp_cut);
-                  free(ts);
                   tmp_len = tmp_cut;
                }
              evas_common_text_props_bidi_set(&ti->text_props,
                    ti->parent.text_node->bidi_props, ti->parent.text_pos);
              evas_common_text_props_script_set (&ti->text_props,
-                   ti->text, tmp_len);
+                   GET_ITEM_TEXT(ti), tmp_len);
              if (ti->parent.format->font.font)
                {
                   c->ENFN->font_text_props_info_create(c->ENDT,
                         ti->parent.format->font.font,
-                        ti->text, &ti->text_props,
+                        GET_ITEM_TEXT(ti), &ti->text_props,
                         ti->parent.text_node->bidi_props,
                         ti->parent.text_pos, tmp_len);
                }
@@ -3050,11 +3048,12 @@ _layout_get_charwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
       const Evas_Object_Textblock_Text_Item *ti)
 {
    int wrap;
+   const Eina_Unicode *str = GET_ITEM_TEXT(ti);
 
    wrap = _layout_text_cutoff_get(c, fmt, ti);
    if (wrap == 0)
-     GET_NEXT(ti->text, wrap);
-   if (!ti->text[wrap])
+     GET_NEXT(str, ti, wrap);
+   if ((wrap < 0) || IS_AT_END(ti, (size_t) wrap))
      wrap = -1;
 
    return wrap;
@@ -3068,20 +3067,21 @@ _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
    int wrap = -1, twrap;
    int orig_wrap;
    Eina_Unicode ch;
-   const Eina_Unicode *str = ti->text;
+   const Eina_Unicode *str = GET_ITEM_TEXT(ti);
+   /* FIXME-tom: a lot of str[] to check if NULL, need to fix that. */
 
    wrap = _layout_text_cutoff_get(c, fmt, ti);
    /* Avoiding too small textblocks to even contain one char */
    if (wrap == 0)
-     GET_NEXT(str, wrap);
+     GET_NEXT(str, ti, wrap);
    orig_wrap = wrap;
    /* We need to wrap and found the position that overflows */
    if (wrap > 0)
      {
-        int index = wrap;
-        ch = GET_NEXT(str, index);
+        size_t index = (size_t) wrap;
+        ch = GET_NEXT(str, ti, index);
         if (!_is_white(ch))
-          wrap = _layout_word_start(str, wrap);
+          wrap = _layout_word_start(ti, wrap);
         /* If we found where to cut the text at, i.e the start
          * of the word we were pointing at */
         if (wrap > 0)
@@ -3097,7 +3097,7 @@ _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
              else
                {
                   /* walk back to start of word */
-                  twrap = _layout_word_start(str, wrap);
+                  twrap = _layout_word_start(ti, wrap);
                   if (twrap != 0)
                     {
                        wrap = twrap;
@@ -3113,7 +3113,7 @@ _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
           {
              /* wrap now is the index of the word START */
              index = wrap;
-             ch = GET_NEXT(str, index);
+             ch = GET_NEXT(str, ti, index);
 
              /* If there are already items in this line, we
               * should just try creating a new line for it */
@@ -3128,17 +3128,18 @@ _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
                {
                   if (mixed_wrap)
                     {
-                       return (str[orig_wrap]) ? orig_wrap : -1;
+                       return ((orig_wrap >= 0) &&
+                             !IS_AT_END(ti, (size_t) orig_wrap)) ? orig_wrap : -1;
                     }
                   else
                     {
                        wrap = 0;
-                       twrap = _layout_word_end(ti->text, wrap);
+                       twrap = _layout_word_end(ti, wrap);
                        wrap = twrap;
                        if (twrap >= 0)
                          {
-                            ch = GET_NEXT(str, wrap);
-                            return (str[wrap]) ? wrap : -1;
+                            ch = GET_NEXT(str, ti, wrap);
+                            return (!IS_AT_END(ti, (size_t) wrap)) ? wrap : -1;
                          }
                        else
                          {
@@ -3165,17 +3166,17 @@ _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
           }
         else
           {
-             twrap = _layout_word_end(ti->text, wrap);
-             wrap = _layout_word_next(ti->text, wrap);
+             twrap = _layout_word_end(ti, wrap);
+             wrap = _layout_word_next(ti, wrap);
              if (wrap >= 0)
                {
-                  ch = GET_NEXT(str, wrap);
-                  return (str[wrap]) ? wrap : -1;
+                  ch = GET_NEXT(str, ti, wrap);
+                  return (!IS_AT_END(ti, (size_t) wrap)) ? wrap : -1;
                }
              else if (twrap >= 0)
                {
-                  ch = GET_NEXT(str, twrap);
-                  return (str[twrap]) ? twrap : -1;
+                  ch = GET_NEXT(str, ti, twrap);
+                  return (!IS_AT_END(ti, (size_t) twrap)) ? twrap : -1;
                }
           }
      }
@@ -3208,8 +3209,7 @@ _layout_ellipsis_item_new(Ctxt *c, const Evas_Object_Textblock_Item *cur_it)
     * the only reason it may not have, is more </> than <>, other
     * than that, we're safe. The last item is the base format. */
    ellip_ti = _layout_text_item_new(c,
-         eina_list_data_get(eina_list_last(c->format_stack)),
-         _ellip_str, len);
+         eina_list_data_get(eina_list_last(c->format_stack)));
    ellip_ti->parent.text_node = cur_it->text_node;
    ellip_ti->parent.text_pos = cur_it->text_pos;
    if (cur_it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
@@ -3224,10 +3224,10 @@ _layout_ellipsis_item_new(Ctxt *c, const Evas_Object_Textblock_Item *cur_it)
    evas_common_text_props_bidi_set(&ellip_ti->text_props,
          ellip_ti->parent.text_node->bidi_props, ellip_ti->parent.text_pos);
    evas_common_text_props_script_set (&ellip_ti->text_props,
-         ellip_ti->text, len);
+         _ellip_str, len);
    c->ENFN->font_text_props_info_create(c->ENDT,
          ellip_ti->parent.format->font.font,
-         ellip_ti->text, &ellip_ti->text_props,
+         _ellip_str, &ellip_ti->text_props,
          ellip_ti->parent.text_node->bidi_props,
          ellip_ti->parent.text_pos, len);
    _text_item_update_sizes(c, ellip_ti);
@@ -3340,7 +3340,7 @@ _layout_visualize_par(Ctxt *c)
                     {
                        wrap = _layout_text_cutoff_get(c, last_it->format,
                              last_ti);
-                       if ((wrap > 0) && last_ti->text[wrap])
+                       if ((wrap > 0) && !IS_AT_END(last_ti, (size_t) wrap))
                          {
                             _layout_item_text_split_strip_white(c, last_ti, i,
                                   wrap);
@@ -5234,7 +5234,7 @@ evas_textblock_cursor_char_next(Evas_Textblock_Cursor *cur)
 
    index = cur->pos;
    text = eina_ustrbuf_string_get(cur->node->unicode);
-   GET_NEXT(text, index);
+   if (text[index]) index++;
    /* Only allow pointing a null if it's the last paragraph.
     * because we don't have a PS there. */
    if (text[index])
@@ -5368,7 +5368,7 @@ evas_textblock_cursor_line_char_last(Evas_Textblock_Cursor *cur)
         if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
           {
              index = _ITEM_TEXT(it)->text_props.text_len - 1;
-             GET_NEXT(_ITEM_TEXT(it)->text, index);
+             if (!IS_AT_END(_ITEM_TEXT(it), index)) index++;
              cur->pos += index;
           }
         else if (!EINA_INLIST_GET(ln)->next && !EINA_INLIST_GET(ln->par)->next)
@@ -6496,7 +6496,9 @@ evas_textblock_cursor_char_delete(Evas_Textblock_Cursor *cur)
 
    text = eina_ustrbuf_string_get(n->unicode);
    index = cur->pos;
-   chr = GET_NEXT(text, index);
+   if (text[index])
+      chr = text[index++];
+
    if (chr == 0) return;
    ppos = cur->pos;
    /* Remove a format node if needed, and remove the char only if the
@@ -7926,7 +7928,8 @@ evas_object_textblock_render(Evas_Object *obj, void *output, void *context, void
    if (ti->parent.format->font.font) ENFN->font_draw(output, context, surface, ti->parent.format->font.font, \
          obj->cur.geometry.x + ln->par->x + ln->x + ti->parent.x + x + (ox), \
          obj->cur.geometry.y + ln->par->y + ln->y + yoff + y + (oy), \
-         ti->parent.w, ti->parent.h, ti->parent.w, ti->parent.h, ti->text, &ti->text_props);
+         ti->parent.w, ti->parent.h, ti->parent.w, ti->parent.h, \
+         GET_ITEM_TEXT(ti), &ti->text_props);
 #define ITEM_WALK_LINE_SKIP_DROP() \
    if ((ln->par->y + ln->y + ln->h) <= 0) continue; \
    if (ln->par->y + ln->y > obj->cur.geometry.h) break
@@ -8443,7 +8446,7 @@ pitem(Evas_Object_Textblock_Item *it)
    if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
      {
         ti = _ITEM_TEXT(it);
-        printf("Text: '%ls'\n", ti->text);
+        printf("Text: '%*ls'\n", ti->text_props.text_len, GET_ITEM_TEXT(ti));
      }
    else
      {