char *utf8;
Evas_Object_Textblock_Node_Format *format_node;
Evas_BiDi_Paragraph_Props *bidi_props;
+ Eina_Bool dirty : 1;
};
struct _Evas_Object_Textblock_Node_Format
EINA_INLIST;
Evas_Object_Textblock_Line *lines;
Evas_Object_Textblock_Node_Text *text_node;
+ Eina_List *logical_items;
int x, y, w, h;
int line_no;
};
* @param ln the layout line to be freed, must not be NULL.
*/
static void
-_line_free(const Evas_Object *obj, Evas_Object_Textblock_Line *ln)
+_line_free(const Evas_Object *obj __UNUSED__, Evas_Object_Textblock_Line *ln)
{
- while (ln->items)
- {
- Evas_Object_Textblock_Item *it = ln->items;
- _item_free(obj, ln, it);
- }
+ /* Items are freed from the logical list */
if (ln) free(ln);
}
Evas_Object_Textblock_Line *ln;
Eina_List *format_stack;
+ Evas_Object_Textblock_Format *fmt;
int x, y;
int w, h;
_layout_paragraph_new(Ctxt *c, Evas_Object_Textblock_Node_Text *n)
{
c->par = calloc(1, sizeof(Evas_Object_Textblock_Paragraph));
- if (c->paragraphs)
- {
- Evas_Object_Textblock_Paragraph *last_par;
- last_par = (Evas_Object_Textblock_Paragraph *)
- EINA_INLIST_GET(c->paragraphs)->last;
- c->par->y = last_par->y + last_par->h;
- }
c->paragraphs = (Evas_Object_Textblock_Paragraph *)eina_inlist_append(EINA_INLIST_GET(c->paragraphs), EINA_INLIST_GET(c->par));
c->ln = NULL;
c->par->text_node = n;
par->lines = (Evas_Object_Textblock_Line *)eina_inlist_remove(EINA_INLIST_GET(par->lines), EINA_INLIST_GET(par->lines));
_line_free(obj, ln);
}
+
+ {
+ Eina_List *i, *i_prev;
+ Evas_Object_Textblock_Item *it;
+ EINA_LIST_FOREACH_SAFE(par->logical_items, i, i_prev, it)
+ {
+ _item_free(obj, NULL, it);
+ }
+ eina_list_free(par->logical_items);
+ }
free(par);
}
return c->align;
}
-#ifdef BIDI_SUPPORT
/**
* @internal
* Reorder the items in a line.
* @param line the line to reorder
*/
static void
-_layout_line_reorder(Ctxt *c __UNUSED__, Evas_Object_Textblock_Line *line)
+_layout_line_order(Ctxt *c __UNUSED__, Evas_Object_Textblock_Line *line)
{
/*FIXME: do it a bit more efficient - not very efficient ATM. */
+#ifdef BIDI_SUPPORT
Evas_Object_Textblock_Item *it;
- Evas_BiDi_Paragraph_Props *props;
EvasBiDiStrIndex *v_to_l = NULL;
size_t start, end;
size_t len;
if (line->items && line->items->text_node &&
line->items->text_node->bidi_props)
{
+ Evas_BiDi_Paragraph_Props *props;
props = line->items->text_node->bidi_props;
start = end = line->items->text_pos;
- }
- else
- {
- return;
- }
- /* Find the first and last positions in the line */
+ /* Find the first and last positions in the line */
- EINA_INLIST_FOREACH(line->items, it)
- {
- if (it->text_pos < start)
+ EINA_INLIST_FOREACH(line->items, it)
{
- start = it->text_pos;
- }
- else
- {
- int tlen;
- tlen = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
- eina_unicode_strlen(_ITEM_TEXT(it)->text) : 1;
- if (it->text_pos + tlen > end)
+ if (it->text_pos < start)
{
- end = it->text_pos + tlen;
+ start = it->text_pos;
+ }
+ else
+ {
+ int tlen;
+ tlen = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
+ eina_unicode_strlen(_ITEM_TEXT(it)->text) : 1;
+ if (it->text_pos + tlen > end)
+ {
+ end = it->text_pos + tlen;
+ }
}
}
- }
- len = end - start;
- evas_bidi_props_reorder_line(NULL, start, len, props, &v_to_l);
+ len = end - start;
+ evas_bidi_props_reorder_line(NULL, start, len, props, &v_to_l);
- /* Update visual pos */
- {
- Evas_Object_Textblock_Item *i;
- i = line->items;
- while (i)
+ /* Update visual pos */
{
- i->visual_pos = evas_bidi_position_logical_to_visual(
- v_to_l, len, i->text_pos - start);
- i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(i)->next;
+ Evas_Object_Textblock_Item *i;
+ i = line->items;
+ while (i)
+ {
+ i->visual_pos = evas_bidi_position_logical_to_visual(
+ v_to_l, len, i->text_pos - start);
+ i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(i)->next;
+ }
}
- }
- /*FIXME: not very efficient, sort the items arrays. Anyhow, should only
- * reorder if it's a bidi paragraph */
- {
- Evas_Object_Textblock_Item *i, *j, *min;
- i = line->items;
- while (i)
+ /*FIXME: not very efficient, sort the items arrays. Anyhow, should only
+ * reorder if it's a bidi paragraph */
{
- min = i;
- EINA_INLIST_FOREACH(i, j)
+ Evas_Object_Textblock_Item *i, *j, *min;
+ i = line->items;
+ while (i)
{
- if (j->visual_pos < min->visual_pos)
+ min = i;
+ EINA_INLIST_FOREACH(i, j)
{
- min = j;
+ if (j->visual_pos < min->visual_pos)
+ {
+ min = j;
+ }
+ }
+ if (min != i)
+ {
+ line->items = (Evas_Object_Textblock_Item *) eina_inlist_remove(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min));
+ line->items = (Evas_Object_Textblock_Item *) eina_inlist_prepend_relative(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min), EINA_INLIST_GET(i));
}
- }
- if (min != i)
- {
- line->items = (Evas_Object_Textblock_Item *) eina_inlist_remove(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min));
- line->items = (Evas_Object_Textblock_Item *) eina_inlist_prepend_relative(EINA_INLIST_GET(line->items), EINA_INLIST_GET(min), EINA_INLIST_GET(i));
- }
- i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(min)->next;
+ i = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(min)->next;
+ }
}
}
+#endif
/* Recalculate the positions according to the new order. */
{
}
}
- /* Fix the order in each line correctly */
+#ifdef BIDI_SUPPORT
if (v_to_l) free(v_to_l);
-}
#endif
+}
/**
* @internal
Evas_Object_Textblock_Item *it;
Eina_Bool no_text = EINA_TRUE;
-#ifdef BIDI_SUPPORT
- _layout_line_reorder(c, c->ln);
-#endif
+ _layout_line_order(c, c->ln);
c->maxascent = c->maxdescent = 0;
EINA_INLIST_FOREACH(c->ln->items, it)
* @param it the item itself.
*/
static void
-_layout_text_add_item(Ctxt *c, Evas_Object_Textblock_Format *fmt,
+_layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Format *fmt,
Evas_Object_Textblock_Text_Item *ti)
{
int tw, th, adv, inset;
inset = c->ENFN->font_inset_get(c->ENDT, fmt->font.font,
ti->text);
ti->inset = inset;
- ti->parent.x = c->x;
adv = 0;
if (fmt->font.font)
adv = c->ENFN->font_h_advance_get(c->ENDT, fmt->font.font,
ti->text, &ti->parent.text_props);
ti->parent.adv = adv;
- c->x += adv;
- c->ln->items = (Evas_Object_Textblock_Item *)
- eina_inlist_append(EINA_INLIST_GET(c->ln->items),
- EINA_INLIST_GET(_ITEM(ti)));
+ ti->parent.x = 0;
+ c->par->logical_items = eina_list_append(c->par->logical_items, ti);
}
/**
static void
_layout_text_append(Ctxt *c, Evas_Object_Textblock_Format *fmt, Evas_Object_Textblock_Node_Text *n, int start, int off, const char *repch)
{
- int tw, new_line, empty_item;
- int wrap, twrap, ch, index, white_stripped;
+ int new_line, empty_item;
Eina_Unicode *alloc_str = NULL;
const Eina_Unicode *str = EINA_UNICODE_EMPTY_STRING;
const Eina_Unicode *tbase;
{
/* if this is the first line item and it starts with spaces - remove them */
int tmp_len;
- wrap = 0;
- white_stripped = 0;
ti = _layout_text_item_new(c, fmt, str);
ti->parent.text_node = n;
&ti->parent.text_props, ti->parent.text_node->bidi_props,
ti->parent.text_pos, tmp_len);
}
- tw = 0;
- if (fmt->font.font)
- tw = c->ENFN->font_h_advance_get(c->ENDT, ti->format->font.font,
- ti->text, &ti->parent.text_props);
- /* Check if we need to wrap, i.e the text is bigger than the width
- * Only calculate wrapping if the width of the object is > 0 */
- if ((c->w >= 0) &&
- ((fmt->wrap_word) || (fmt->wrap_char)) &&
- ((c->x + tw) >
- (c->w - c->o->style_pad.l - c->o->style_pad.r -
- c->marginl - c->marginr)))
- {
- wrap = _layout_text_cutoff_get(c, fmt, ti);
- /* Avoiding too small textblocks to even contain one char */
- if (wrap == 0)
- GET_NEXT(str, wrap);
- /* We need to wrap and found the position that overflows */
- if (wrap > 0)
- {
- if (fmt->wrap_word)
- {
- index = wrap;
- ch = GET_NEXT(str, index);
- if (!_is_white(ch))
- wrap = _layout_word_start(str, wrap);
- /* If we found where to cut the text at, i.e the start
- * of the word we were pointing at */
- if (wrap > 0)
- {
- twrap = wrap;
- ch = GET_PREV(str, twrap);
- /* the text intersects the wrap point on a whitespace char */
- if (_is_white(ch))
- {
- _layout_item_text_cutoff(c, ti, wrap);
- twrap = wrap;
- str += twrap;
- }
- /* intersects a word */
- else
- {
- /* walk back to start of word */
- twrap = _layout_word_start(str, wrap);
- if (twrap != 0)
- {
- wrap = twrap;
- ch = GET_PREV(str, twrap);
- _layout_item_text_cutoff(c, ti, twrap);
- str += wrap;
- }
- else
- {
- empty_item = 1;
- _item_free(c->obj, NULL, _ITEM(ti));
- if (c->ln->items)
- {
- ti = _ITEM_TEXT((EINA_INLIST_GET(c->ln->items))->last);
- _layout_strip_trailing_whitespace(c, fmt, _ITEM(ti));
- twrap = _layout_word_end(str, wrap);
- if (twrap >= 0)
- {
- ch = GET_NEXT(str, twrap);
- str += twrap;
- }
- else
- str = NULL;
- }
- }
- }
- }
- /* If we weren't able to find the start of the word we
- * are currently pointing at, or we were able but it's
- * the first word */
- else
- {
- /* wrap now is the index of the word START */
- index = wrap;
- ch = GET_NEXT(str, index);
+ str += tmp_len;
- /* If there are already items in this line, we
- * should just try creating a new line for it */
- if (c->ln->items)
- {
- white_stripped = _layout_item_abort(c, fmt, _ITEM(ti));
- empty_item = 1;
- }
- /* If there were no items in this line, try to do
- * our best wrapping possible since it's the middle
- * of the word */
- else
- {
- wrap = 0;
- twrap = _layout_word_end(ti->text, wrap);
- wrap = twrap;
- if (twrap >= 0)
- {
- ch = GET_NEXT(str, wrap);
- _layout_item_text_cutoff(c, ti, twrap);
- }
- if (wrap > 0)
- str += wrap;
- else
- str = NULL;
- }
- }
- }
- else if (fmt->wrap_char)
- {
- _layout_item_text_cutoff(c, ti, wrap);
- str += wrap;
- }
- /* Marked we wrapped and we want to start a new line */
- new_line = 1;
- }
- /* We need to wrap, but for some reason we failed obatining the
- * overflow position. */
- else
- {
- /*FIXME: sanitize this error handling - should probably
- * never get here anyway unless something really bad
- * has happend */
- /* wrap now is the index of the word START */
- if (wrap < 0) wrap = 0;
- index = wrap;
- ch = GET_NEXT(str, index);
-
- if (c->ln->items)
- {
- white_stripped = _layout_item_abort(c, fmt, _ITEM(ti));
- empty_item = 1;
- new_line = 1;
- }
- else
- {
- if (wrap <= 0)
- {
- wrap = 0;
- twrap = _layout_word_end(ti->text, wrap);
- wrap = _layout_word_next(ti->text, wrap);
- if (twrap >= 0)
- _layout_item_text_cutoff(c, ti, twrap);
- if (wrap >= 0)
- str += wrap;
- else
- str += tmp_len;
- }
- else
- str += tmp_len;
- new_line = 1;
- }
- }
- }
- else
- str += tmp_len;
-
- /* Set item properties */
- if (empty_item)
- {
- empty_item = 0;
- }
- else
- {
- _layout_text_add_item(c, fmt, ti);
- }
-
-
- if (new_line)
- {
- if (str)
- {
- if (!white_stripped)
- {
- index = 0;
- ch = GET_NEXT(str, index);
- if (_is_white(ch)) str += index;
- }
- }
- new_line = 0;
- _layout_line_advance(c, fmt, EINA_TRUE);
- }
+ _layout_text_add_logical_item(c, fmt, ti);
}
if (alloc_str) free(alloc_str);
fi->item = eina_stringshare_add(item);
fi->source_node = n;
fi->parent.type = EVAS_TEXTBLOCK_ITEM_FORMAT;
- c->ln->items = _ITEM(eina_inlist_append(
- EINA_INLIST_GET(c->ln->items),
- EINA_INLIST_GET(_ITEM(fi))));
+ c->par->logical_items = eina_list_append(c->par->logical_items, fi);
if (n)
{
fi->parent.text_node = n->text_node;
x2 = c->x + w;
- if (x2 >
- (c->w - c->o->style_pad.l -
- c->o->style_pad.r -
- c->marginl - c->marginr))
- {
- _layout_line_advance(c, fmt, EINA_TRUE);
- x2 = w;
- }
fi = _layout_format_item_add(c, n, NULL);
- fi->parent.x = c->x;
fi->vsize = vsize;
fi->size = size;
fi->formatme = 1;
fi->parent.h = h;
fi->ascent = c->maxascent;
fi->descent = c->maxdescent;
- c->x = x2;
handled = 1;
}
if (!handled)
Evas_Object_Textblock_Format_Item *fi;
fi = _layout_format_item_add(c, n, item);
- fi->parent.x = c->x;
fi->parent.w = fi->parent.adv = 0;
- _layout_line_advance(c, fmt, EINA_TRUE);
}
else if ((!strcmp(item, "\t")) || (!strcmp(item, "\\t")))
{
int x2;
x2 = c->x + fmt->tabstops;
- /* Wrap lines if there's a size */
- if ((c->w > 0) && (x2 >
- (c->w - c->o->style_pad.l -
- c->o->style_pad.r -
- c->marginl - c->marginr)))
- {
- _layout_line_advance(c, fmt, EINA_TRUE);
- x2 = c->x + fmt->tabstops;
- }
- if (c->ln->items)
- {
- Evas_Object_Textblock_Item *it;
-
- it = (Evas_Object_Textblock_Item *)(EINA_INLIST_GET(c->ln->items))->last;
- _layout_strip_trailing_whitespace(c, fmt, it);
- }
fi = _layout_format_item_add(c, n, item);
- fi->parent.x = c->x;
fi->parent.w = fi->parent.adv = x2 - c->x;
fi->formatme = 1;
c->x = x2;
*_fmt = fmt;
}
+static void
+_layout_update_par(Ctxt *c)
+{
+ Evas_Object_Textblock_Paragraph *last_par;
+ last_par = (Evas_Object_Textblock_Paragraph *)
+ EINA_INLIST_GET(c->par)->prev;
+ if (last_par)
+ {
+ c->par->y = last_par->y + last_par->h;
+ }
+}
+static void
+_layout_visualize_par(Ctxt *c)
+{
+ Evas_Object_Textblock_Item *it;
+ Eina_List *i;
+ if (!c->par->logical_items)
+ return;
+
+ _layout_line_new(c, c->fmt);
+ EINA_LIST_FOREACH(c->par->logical_items, i, it)
+ {
+ c->ln->items = (Evas_Object_Textblock_Item *)
+ eina_inlist_append(EINA_INLIST_GET(c->ln->items),
+ EINA_INLIST_GET(it));
+ /* FIXME: Add word wrappnig here */
+ if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
+ {
+ Evas_Object_Textblock_Format_Item *fi;
+ fi = _ITEM_FORMAT(it);
+ fi->y = c->y;
+ if (_IS_LINE_SEPARATOR(
+ eina_strbuf_string_get(fi->source_node->format)))
+ {
+ _layout_line_advance(c, c->fmt, EINA_TRUE);
+ }
+ }
+ else
+ {
+ c->fmt = _ITEM_TEXT(it)->format;
+ }
+ }
+ _layout_line_advance(c, c->fmt, EINA_FALSE);
+}
+
/**
* @internal
* Create the layout from the nodes.
int off;
_layout_paragraph_new(c, n); /* Each node is a paragraph */
- _layout_line_new(c, fmt);
/* For each text node to thorugh all of it's format nodes
* append text from the start to the offset of the next format
_layout_text_append(c, fmt, n, start, -1, o->repch);
}
+ /* FIXME: move away? */
+ {
+ /* FIXME: is this the right format? or maybe it can change with pops? */
+ c->fmt = c->format_stack->data;
+ Evas_Object_Textblock_Paragraph *par;
+ EINA_INLIST_FOREACH(c->paragraphs, par)
+ {
+ c->par = par;
+ _layout_update_par(c);
+ _layout_visualize_par(c);
+ }
+ }
+
/* Advance the line so it'll calculate the size */
if ((c->ln) && (c->ln->items) && (fmt))
- _layout_line_advance(c, fmt, EINA_TRUE);
+ //_layout_line_advance(c, fmt, EINA_TRUE);
/* Clean the rest of the format stack */
while (c->format_stack)
GET_NEXT(_ITEM_TEXT(it)->text, index);
cur->pos += index;
}
- else if (!EINA_INLIST_GET(ln)->next)
+ else if (!EINA_INLIST_GET(ln)->next && !EINA_INLIST_GET(ln->par)->next)
{
cur->pos++;
}
n = calloc(1, sizeof(Evas_Object_Textblock_Node_Text));
n->unicode = eina_ustrbuf_new();
+ /* We want to layout each paragraph at least once. */
+ n->dirty = EINA_TRUE;
#ifdef BIDI_SUPPORT
n->bidi_props = evas_bidi_paragraph_props_new();
n->bidi_props->direction = EVAS_BIDI_PARAGRAPH_NATURAL;
}
/**
+ * @internal
+ * Mark and notifiy that the textblock, and specifically a node has changed.
+ *
+ * @param o the textblock object.
+ * @param obj the evas object.
+ * @param n the paragraph that changed.
+ */
+static void
+_evas_textblock_content_changed(Evas_Object_Textblock *o, Evas_Object *obj,
+ Evas_Object_Textblock_Node_Text *n)
+{
+ n->dirty = EINA_TRUE;
+ _evas_textblock_changed(o, obj);
+}
+
+/**
* Adds text to the current cursor position and set the cursor to *before*
* the start of the text just added.
*