Evas textblock: Made selection rtl friendly. There are still minor issues with rectangle positioning (1px off sometimes) but this is better than before.

SVN revision: 56415
This commit is contained in:
Tom Hacohen 2011-01-30 10:32:15 +00:00
parent 62840fe30b
commit 9f36a734b2
1 changed files with 255 additions and 67 deletions

View File

@ -7091,6 +7091,226 @@ evas_textblock_cursor_line_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord y)
return -1;
}
/**
* @internal
* Returns the geometry of the range in line ln. Cur1 is the start cursor,
* cur2 is the end cursor, NULL means from the start or to the end accordingly.
* Assumes that ln is valid, and that at least one of cur1 and cur2 is not NULL.
*
* @param ln the line to work on.
* @param cur1 the start cursor
* @param cur2 the end cursor
* @return Returns the geometry of the range
*/
static Eina_List *
_evas_textblock_cursor_range_in_line_geometry_get(
const Evas_Object_Textblock_Line *ln, const Evas_Textblock_Cursor *cur1,
const Evas_Textblock_Cursor *cur2)
{
Evas_Object_Textblock_Item *it;
Evas_Object_Textblock_Item *it1, *it2;
Eina_List *rects = NULL;
Evas_Textblock_Rectangle *tr;
size_t start, end;
const Evas_Textblock_Cursor *cur;
cur = (cur1) ? cur1 : cur2;
/* Find the first and last items */
it1 = it2 = NULL;
start = end = 0;
EINA_INLIST_FOREACH(ln->items, it)
{
size_t item_len;
item_len = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
eina_unicode_strlen(((Evas_Object_Textblock_Text_Item *) it)->text)
: 1;
if ((!cur1 || (cur1->pos < it->text_pos + item_len)) &&
(!cur2 || (cur2->pos >= it->text_pos)))
{
if (!it1)
{
it1 = it;
start = (cur1) ? (cur1->pos - it->text_pos) : 0;
}
it2 = it;
end = (cur2) ? (cur2->pos - it->text_pos) : item_len;
}
}
/* Special case when they share the same item. */
if (it1 == it2)
{
Evas_Coord x1, w1, x2, w2;
Evas_Coord x, w, y, h;
if (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT)
{
Evas_Object_Textblock_Text_Item *ti;
int ret;
ti = _ITEM_TEXT(it1);
ret = cur->ENFN->font_char_coords_get(cur->ENDT,
ti->format->font.font,
ti->text, &ti->bidi_props,
start,
&x1, &y, &w1, &h);
if (!ret)
{
return NULL;
}
ret = cur->ENFN->font_char_coords_get(cur->ENDT,
ti->format->font.font,
ti->text, &ti->bidi_props,
end,
&x2, &y, &w2, &h);
if (!ret)
{
return NULL;
}
/* This also takes bidi into account because of the positions
* in the text are taken into account */
if (x2 > x1)
{
w = x2 - x1;
x = x1;
}
else
{
w = x1 - x2;
x = x2;
}
}
else
{
x = 0;
w = it1->w;
}
if (w > 0)
{
tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
rects = eina_list_append(rects, tr);
tr->x = ln->x + it1->x + x;
tr->y = ln->y;
tr->h = ln->h;
tr->w = w;
}
}
else
{
/* Get the middle items */
Evas_Coord min_x, max_x;
Evas_Coord x, w;
it = _ITEM(EINA_INLIST_GET(it1)->next);
min_x = max_x = it->x;
if (it1->type == EVAS_TEXTBLOCK_ITEM_TEXT)
{
Evas_Coord y, h;
Evas_Object_Textblock_Text_Item *ti;
int ret;
ti = _ITEM_TEXT(it1);
ret = cur->ENFN->font_char_coords_get(cur->ENDT,
ti->format->font.font,
ti->text, &ti->bidi_props,
start,
&x, &y, &w, &h);
if (!ret)
{
/* BUG! Skip the first item */
x = w = 0;
}
else
{
#ifdef BIDI_SUPPORT
if (evas_bidi_is_rtl_char(&ti->bidi_props, 0))
{
w = x;
x = 0;
}
else
#endif
{
w = it1->w - x;
}
}
}
else
{
x = 0;
w = it1->w;
}
if (w > 0)
{
tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
rects = eina_list_append(rects, tr);
tr->x = ln->x + it1->x + x;
tr->y = ln->y;
tr->h = ln->h;
tr->w = w;
}
while (it && (it != it2))
{
max_x = it->x + it->w;
it = (Evas_Object_Textblock_Item *) EINA_INLIST_GET(it)->next;
}
if (min_x != max_x)
{
tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
rects = eina_list_append(rects, tr);
tr->x = ln->x + min_x;
tr->y = ln->y;
tr->h = ln->h;
tr->w = max_x - min_x;
}
if (it2->type == EVAS_TEXTBLOCK_ITEM_TEXT)
{
Evas_Coord y, h;
Evas_Object_Textblock_Text_Item *ti;
int ret;
ti = _ITEM_TEXT(it2);
ret = cur->ENFN->font_char_coords_get(cur->ENDT,
ti->format->font.font,
ti->text, &ti->bidi_props,
end,
&x, &y, &w, &h);
if (!ret)
{
/* BUG! skip the last item */
x = w = 0;
}
else
{
#ifdef BIDI_SUPPORT
if (evas_bidi_is_rtl_char(&ti->bidi_props, 0))
{
w = it2->w - x;
}
else
#endif
{
w = x;
x = 0;
}
}
}
else
{
x = 0;
w = it2->w;
}
if (w > 0)
{
tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
rects = eina_list_append(rects, tr);
tr->x = ln->x + it2->x + x;
tr->y = ln->y;
tr->h = ln->h;
tr->w = w;
}
}
return rects;
}
/**
* Get the geometry of a range.
*
@ -7101,14 +7321,17 @@ evas_textblock_cursor_line_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord y)
EAPI Eina_List *
evas_textblock_cursor_range_geometry_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
{
Evas_Object_Textblock *o;
Evas_Object_Textblock_Line *ln1, *ln2;
Evas_Object_Textblock_Item *it1, *it2;
Eina_List *rects = NULL;
Evas_Coord cx, cy, cw, ch, lx, ly, lw, lh;
Evas_Textblock_Rectangle *tr;
int i, line, line2;
if (!cur1) return NULL;
if (!cur2) return NULL;
if (!cur1 || !cur1->node) return NULL;
if (!cur2 || !cur2->node) return NULL;
if (cur1->obj != cur2->obj) return NULL;
o = (Evas_Object_Textblock *)(cur1->obj->object_data);
if (!o->formatted.valid) _relayout(cur1->obj);
if (evas_textblock_cursor_compare(cur1, cur2) > 0)
{
const Evas_Textblock_Cursor *tc;
@ -7118,76 +7341,41 @@ evas_textblock_cursor_range_geometry_get(const Evas_Textblock_Cursor *cur1, cons
cur2 = tc;
}
line = evas_textblock_cursor_char_geometry_get(cur1, &cx, &cy, &cw, &ch);
if (line < 0) return NULL;
line = evas_textblock_cursor_line_geometry_get(cur1, &lx, &ly, &lw, &lh);
if (line < 0) return NULL;
line2 = evas_textblock_cursor_line_geometry_get(cur2, NULL, NULL, NULL, NULL);
if (line2 < 0) return NULL;
ln1 = ln2 = NULL;
it1 = it2 = NULL;
_find_layout_item_match(cur1, &ln1, &it1);
if (!ln1 || !it1) return NULL;
_find_layout_item_match(cur2, &ln2, &it2);
if (!ln2 || !it2) return NULL;
if (line == line2)
if (ln1 == ln2)
{
tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
rects = eina_list_append(rects, tr);
tr->x = cx;
tr->y = ly;
tr->h = lh;
line = evas_textblock_cursor_char_geometry_get(cur2, &cx, &cy, &cw, &ch);
if (line < 0)
{
while (rects)
{
free(rects->data);
rects = eina_list_remove_list(rects, rects);
}
return NULL;
}
tr->w = cx - tr->x;
rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
cur1, cur2);
}
else
{
tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
rects = eina_list_append(rects, tr);
tr->x = cx;
tr->y = ly;
tr->h = lh;
tr->w = lx + lw - cx;
for (i = line +1; i < line2; i++)
{
evas_object_textblock_line_number_geometry_get(cur1->obj, i, &lx, &ly, &lw, &lh);
Evas_Object_Textblock_Line *lni;
Eina_List *rects2 = NULL;
/* Handle the first line */
rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1,
cur1, NULL);
/* Handle the lines between the first and the last line */
lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(ln1)->next;
while (lni && (lni != ln2))
{
tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
rects = eina_list_append(rects, tr);
tr->x = lx;
tr->y = ly;
tr->h = lh;
tr->w = lw;
}
line = evas_textblock_cursor_char_geometry_get(cur2, &cx, &cy, &cw, &ch);
if (line < 0)
{
while (rects)
{
free(rects->data);
rects = eina_list_remove_list(rects, rects);
}
return NULL;
}
line = evas_textblock_cursor_line_geometry_get(cur2, &lx, &ly, &lw, &lh);
if (line < 0)
{
while (rects)
{
free(rects->data);
rects = eina_list_remove_list(rects, rects);
}
return NULL;
}
tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
rects = eina_list_append(rects, tr);
tr->x = lx;
tr->y = ly;
tr->h = lh;
tr->w = cx - lx;
tr->x = lni->x;
tr->y = lni->y;
tr->h = lni->h;
tr->w = lni->w;
lni = (Evas_Object_Textblock_Line *) EINA_INLIST_GET(lni)->next;
}
rects2 = _evas_textblock_cursor_range_in_line_geometry_get(ln2,
NULL, cur2);
rects = eina_list_merge(rects, rects2);
}
return rects;
}