static void
_sel_update(Edje *ed, Evas_Textblock_Cursor *c EINA_UNUSED, Evas_Object *o, Entry *en)
{
- Eina_List *range = NULL, *l;
- Sel *sel;
- Evas_Coord x, y, w, h;
+ Evas_Coord x, y;
Evas_Object *smart, *clip;
smart = evas_object_smart_parent_get(o);
clip = evas_object_clip_get(o);
- if (en->sel_start)
- range = evas_textblock_cursor_range_geometry_get(en->sel_start, en->sel_end);
- else
- return;
- if (eina_list_count(range) != eina_list_count(en->sel))
+ if (!en->sel_start)
+ return;
+
+ evas_object_geometry_get(o, &x, &y, NULL, NULL);
+ if (en->have_selection)
{
- while (en->sel)
- {
- sel = en->sel->data;
- if (sel->obj_bg) evas_object_del(sel->obj_bg);
- if (sel->obj_fg) evas_object_del(sel->obj_fg);
- free(sel);
- en->sel = eina_list_remove_list(en->sel, en->sel);
- }
- if (en->have_selection)
+ Eina_Iterator *range = NULL;
+ Eina_List *l;
+ Sel *sel;
+ Evas_Textblock_Rectangle *r;
+
+ range = evas_textblock_cursor_range_simple_geometry_get(en->sel_start,
+ en->sel_end);
+
+ l = en->sel;
+ EINA_ITERATOR_FOREACH(range, r)
{
- for (l = range; l; l = eina_list_next(l))
+ if (!l)
{
Evas_Object *ob;
sel->obj_fg = ob;
_edje_subobj_register(ed, sel->obj_fg);
}
- }
- }
- x = y = w = h = -1;
- evas_object_geometry_get(o, &x, &y, &w, &h);
- if (en->have_selection)
- {
- EINA_LIST_FOREACH(en->sel, l, sel)
- {
- Evas_Textblock_Rectangle *r;
+ else
+ {
+ sel = eina_list_data_get(l);
+ l = l->next;
+ }
+ *(&(sel->rect)) = *r;
- r = range->data;
if (sel->obj_bg)
{
evas_object_move(sel->obj_bg, x + r->x, y + r->y);
evas_object_move(sel->obj_fg, x + r->x, y + r->y);
evas_object_resize(sel->obj_fg, r->w, r->h);
}
- *(&(sel->rect)) = *r;
- range = eina_list_remove_list(range, range);
free(r);
}
- }
- else
- {
- while (range)
+ eina_iterator_free(range);
+
+ /* delete redundant selection rects */
+ while (l)
{
- free(range->data);
- range = eina_list_remove_list(range, range);
+ Eina_List *temp = l->next;
+ sel = eina_list_data_get(l);
+ if (sel)
+ {
+ if (sel->obj_bg) evas_object_del(sel->obj_bg);
+ if (sel->obj_fg) evas_object_del(sel->obj_fg);
+ free(sel);
+ }
+ en->sel = eina_list_remove_list(en->sel, l);
+ l = temp;
}
}
}
*/
EAPI Eina_List *evas_textblock_cursor_range_geometry_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1, 2);
+/**
+ * Get the simple geometry of a range.
+ * The simple geometry is the geomtry in which rectangles in middle
+ * lines of range are merged into one big rectangle.
+ *
+ * @param cur1 one side of the range.
+ * @param cur2 other side of the range.
+ * @return an iterator of rectangles representing the geometry of the range.
+ */
+EAPI Eina_Iterator *evas_textblock_cursor_range_simple_geometry_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1, 2);
+
/**
* Get the geometry of ?
*
* A textblock format.
*/
typedef struct _Evas_Object_Textblock_Format Evas_Object_Textblock_Format;
-
+/**
+ * @internal
+ * @typedef Evas_Textblock_Selection_Iterator
+ * A textblock selection iterator.
+ */
+typedef struct _Evas_Textblock_Selection_Iterator Evas_Textblock_Selection_Iterator;
/**
* @internal
* @def IS_AT_END(ti, ind)
Eina_Bool legacy_newline : 1;
};
+struct _Evas_Textblock_Selection_Iterator
+{
+ Eina_Iterator iterator; /**< Eina Iterator. */
+ Eina_List *list; /**< Head of list. */
+ Eina_List *current; /**< Current node in loop. */
+};
+
/* private methods for textblock objects */
static void evas_object_textblock_init(Evas_Object *eo_obj);
static void evas_object_textblock_render(Evas_Object *eo_obj,
static void _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Text *n, size_t start, int offset);
static void _evas_textblock_cursors_set_node(Evas_Textblock_Data *o, const Evas_Object_Textblock_Node_Text *n, Evas_Object_Textblock_Node_Text *new_node);
+/** selection iterator */
+/**
+ * @internal
+ * Returns the value of the current data of list node,
+ * and goes to the next list node.
+ *
+ * @param it the iterator.
+ * @param data the data of the current list node.
+ * @return EINA_FALSE if the current list node does not exists.
+ * Otherwise, returns EINA_TRUE.
+ */
+static Eina_Bool
+_evas_textblock_selection_iterator_next(Evas_Textblock_Selection_Iterator *it, void **data)
+{
+ if (!it->current)
+ return EINA_FALSE;
+
+ *data = eina_list_data_get(it->current);
+ it->current = eina_list_next(it->current);
+
+ return EINA_TRUE;
+}
+
+/**
+ * @internal
+ * Gets the iterator container (Eina_List) which created the iterator.
+ * @param it the iterator.
+ * @return A pointer to Eina_List.
+ */
+static Eina_List *
+_evas_textblock_selection_iterator_get_container(Evas_Textblock_Selection_Iterator *it)
+{
+ return it->list;
+}
+
+/**
+ * @internal
+ * Frees the iterator container (Eina_List).
+ * @param it the iterator.
+ */
+static void
+_evas_textblock_selection_iterator_free(Evas_Textblock_Selection_Iterator *it)
+{
+ while (it->list)
+ it->list = eina_list_remove_list(it->list, it->list);
+ EINA_MAGIC_SET(&it->iterator, 0);
+ free(it);
+}
+
+/**
+ * @internal
+ * Creates newly allocated iterator associated to a list.
+ * @param list The list.
+ * @return If the memory cannot be allocated, NULL is returned.
+ * Otherwise, a valid iterator is returned.
+ */
+Eina_Iterator *
+_evas_textblock_selection_iterator_new(Eina_List *list)
+{
+ Evas_Textblock_Selection_Iterator *it;
+
+ it = calloc(1, sizeof(Evas_Textblock_Selection_Iterator));
+ if (!it) return NULL;
+
+ EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
+ it->list = list;
+ it->current = list;
+
+ it->iterator.version = EINA_ITERATOR_VERSION;
+ it->iterator.next = FUNC_ITERATOR_NEXT(
+ _evas_textblock_selection_iterator_next);
+ it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
+ _evas_textblock_selection_iterator_get_container);
+ it->iterator.free = FUNC_ITERATOR_FREE(
+ _evas_textblock_selection_iterator_free);
+
+ return &it->iterator;
+}
+
/* styles */
/**
* @internal
return rects;
}
+EAPI Eina_Iterator *
+evas_textblock_cursor_range_simple_geometry_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
+{
+ Evas_Object_Textblock_Line *ln1, *ln2;
+ Evas_Object_Textblock_Item *it1, *it2;
+ Eina_List *rects = NULL;
+ Eina_Iterator *itr = NULL;
+
+ if (!cur1 || !cur1->node) return NULL;
+ if (!cur2 || !cur2->node) return NULL;
+ if (cur1->obj != cur2->obj) return NULL;
+ Evas_Textblock_Data *o = eo_data_scope_get(cur1->obj, MY_CLASS);
+
+ _relayout_if_needed(cur1->obj, o);
+
+ if (evas_textblock_cursor_compare(cur1, cur2) > 0)
+ {
+ const Evas_Textblock_Cursor *tc;
+
+ tc = cur1;
+ cur1 = cur2;
+ cur2 = tc;
+ }
+
+ 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 (ln1 == ln2)
+ {
+ rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1, cur1, cur2);
+ }
+ else
+ {
+ int lm = 0, rm = 0;
+ Eina_List *rects2 = NULL;
+ Evas_Coord w;
+ Evas_Textblock_Cursor *tc;
+ Evas_Textblock_Rectangle *tr;
+
+ if (ln1->items)
+ {
+ Evas_Object_Textblock_Format *fm = ln1->items->format;
+ if (fm)
+ {
+ lm = fm->margin.l;
+ rm = fm->margin.r;
+ }
+ }
+
+ evas_object_geometry_get(cur1->obj, NULL, NULL, &w, NULL);
+ rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1, cur1, NULL);
+
+ /* Extend selection rectangle in first line */
+ tc = evas_object_textblock_cursor_new(cur1->obj);
+ evas_textblock_cursor_copy(cur1, tc);
+ evas_textblock_cursor_line_char_last(tc);
+ tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
+ evas_textblock_cursor_pen_geometry_get(tc, &tr->x, &tr->y, &tr->w, &tr->h);
+ if (ln1->par->direction == EVAS_BIDI_DIRECTION_RTL)
+ {
+ tr->w = tr->x + tr->w - rm;
+ tr->x = lm;
+ }
+ else
+ {
+ tr->w = w - tr->x - rm;
+ }
+ rects = eina_list_append(rects, tr);
+ evas_textblock_cursor_free(tc);
+
+ rects2 = _evas_textblock_cursor_range_in_line_geometry_get(ln2, NULL, cur2);
+
+ /* Add middle rect */
+ if ((ln1->par->y + ln1->y + ln1->h) != (ln2->par->y + ln2->y))
+ {
+ tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
+ tr->x = lm;
+ tr->y = ln1->par->y + ln1->y + ln1->h;
+ tr->w = w - tr->x - rm;
+ tr->h = ln2->par->y + ln2->y - tr->y;
+ rects = eina_list_append(rects, tr);
+ }
+ rects = eina_list_merge(rects, rects2);
+ }
+ itr = _evas_textblock_selection_iterator_new(rects);
+
+ return itr;
+}
+
EAPI Eina_List *
evas_textblock_cursor_range_geometry_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
{
EINA_LIST_FREE(rects, tr)
free(tr);
+ /* Range simple geometry */
+ /* Single line range */
+ Eina_Iterator *it, *it2;
+ Evas_Textblock_Rectangle *tr3;
+ Evas_Coord w = 200;
+ evas_object_textblock_text_markup_set(tb, "This <br/> is a test.<br/>Another <br/>text.");
+ evas_object_resize(tb, w, w / 2);
+ evas_textblock_cursor_pos_set(cur, 0);
+ evas_textblock_cursor_pos_set(main_cur, 3);
+
+ it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
+ fail_if(!it);
+ rects = eina_iterator_container_get(it);
+ fail_if(!rects);
+ it2 = evas_textblock_cursor_range_simple_geometry_get(main_cur, cur);
+ fail_if(!it2);
+ rects2 = eina_iterator_container_get(it2);
+ fail_if(!rects2);
+
+ fail_if(eina_list_count(rects) != 1);
+ fail_if(eina_list_count(rects2) != 1);
+
+ tr = eina_list_data_get(rects);
+ fail_if((tr->h <= 0) || (tr->w <= 0));
+ tr2 = eina_list_data_get(rects2);
+ fail_if((tr->h <= 0) || (tr->w <= 0));
+
+ fail_if((tr->x != tr2->x) || (tr->y != tr2->y) || (tr->w != tr2->w) ||
+ (tr->h != tr2->h));
+
+ EINA_LIST_FREE(rects, tr)
+ free(tr);
+ EINA_LIST_FREE(rects2, tr2)
+ free(tr2);
+ eina_iterator_free(it);
+ eina_iterator_free(it2);
+
+ /* Multiple range */
+ evas_textblock_cursor_pos_set(cur, 0);
+ evas_textblock_cursor_pos_set(main_cur, 16);
+
+ it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
+ fail_if(!it);
+ rects = eina_iterator_container_get(it);
+ fail_if(!rects);
+ it2 = evas_textblock_cursor_range_simple_geometry_get(main_cur, cur);
+ fail_if(!it2);
+ rects2 = eina_iterator_container_get(it2);
+ fail_if(!rects2);
+
+ fail_if(eina_list_count(rects) != 3);
+ fail_if(eina_list_count(rects2) != 3);
+
+ tr = eina_list_data_get(rects);
+ fail_if((tr->h <= 0) || (tr->w <= 0));
+ tr2 = eina_list_data_get(rects2);
+ fail_if((tr2->h <= 0) || (tr2->w <= 0));
+
+ fail_if((tr->x != tr2->x) || (tr->y != tr2->y) || (tr->w != tr2->w) ||
+ (tr->h != tr2->h));
+
+ tr = eina_list_nth(rects, 1);
+ fail_if((tr->h <= 0) || (tr->w <= 0));
+ tr2 = eina_list_data_get(eina_list_next(rects2));
+ fail_if((tr2->h <= 0) || (tr2->w <= 0));
+
+ fail_if((tr->x != tr2->x) || (tr->y != tr2->y) || (tr->w != tr2->w) ||
+ (tr->h != tr2->h));
+
+ tr = eina_list_nth(rects, 2);
+ fail_if((tr->h <= 0) || (tr->w <= 0));
+ tr2 = eina_list_nth(rects2, 2);
+ fail_if((tr2->h <= 0) || (tr2->w <= 0));
+
+ fail_if((tr->x != tr2->x) || (tr->y != tr2->y) || (tr->w != tr2->w) ||
+ (tr->h != tr2->h));
+
+ /* Check that the second line is positioned below the first */
+ tr = eina_list_data_get(rects);
+ tr2 = eina_list_nth(rects, 1);
+ tr3 = eina_list_nth(rects, 2);
+ fail_if((tr->y >= tr3->y) || (tr2->y >= tr3->y));
+ fail_if(tr2->x + tr2->w != w);
+
+ /* Have middle rectangle */
+ evas_textblock_cursor_pos_set(cur, 0);
+ evas_textblock_cursor_pos_set(main_cur, 31);
+ it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
+ fail_if(!it);
+ rects = eina_iterator_container_get(it);
+ fail_if(!rects);
+ it2 = evas_textblock_cursor_range_simple_geometry_get(main_cur, cur);
+ fail_if(!it2);
+ rects2 = eina_iterator_container_get(it2);
+ fail_if(!rects2);
+
+ fail_if(eina_list_count(rects) != 4);
+ fail_if(eina_list_count(rects) != 4);
+
+ tr = eina_list_data_get(rects);
+ fail_if((tr->h <= 0) || (tr->w <= 0));
+ tr2 = eina_list_data_get(rects2);
+ fail_if((tr2->h <= 0) || (tr2->w <= 0));
+ fail_if((tr->x != tr2->x) || (tr->y != tr2->y) || (tr->w != tr2->w) ||
+ (tr->h != tr2->h));
+
+ tr = eina_list_nth(rects, 1);
+ fail_if((tr->h <= 0) || (tr->w <= 0));
+ tr2 = eina_list_nth(rects2, 1);
+ fail_if((tr2->h <= 0) || (tr2->w <= 0));
+ fail_if((tr->x != tr2->x) || (tr->y != tr2->y) || (tr->w != tr2->w) ||
+ (tr->h != tr2->h));
+
+ tr = eina_list_nth(rects, 2);
+ fail_if((tr->h <= 0) || (tr->w <= 0));
+ tr2 = eina_list_nth(rects2, 2);
+ fail_if((tr2->h <= 0) || (tr2->w <= 0));
+ fail_if((tr->x != tr2->x) || (tr->y != tr2->y) || (tr->w != tr2->w) ||
+ (tr->h != tr2->h));
+
+ tr = eina_list_nth(rects, 3);
+ fail_if((tr->h <= 0) || (tr->w <= 0));
+ tr2 = eina_list_nth(rects2, 3);
+ fail_if((tr2->h <= 0) || (tr2->w <= 0));
+ fail_if((tr->x != tr2->x) || (tr->y != tr2->y) || (tr->w != tr2->w) ||
+ (tr->h != tr2->h));
+
+ /* Check that the middle rectanlge is between first and last rectangles */
+ tr = eina_list_data_get(rects);
+ tr2 = eina_list_nth(rects, 2);
+ tr3 = eina_list_nth(rects, 3);
+ fail_if((tr2->y < tr->y + tr->h) || (tr2->y + tr2->h > tr3->y));
+
+ /* Check that the middle rectangle has proper width */
+ tr = eina_list_data_get(rects);
+ tr2 = eina_list_nth(rects, 1);
+ fail_if((tr->y != tr2->y) || (tr->h != tr2->h));
+ tr3 = eina_list_nth(rects, 2);
+ fail_if((tr2->x + tr2->w != w) || (tr2->x + tr2->w != tr3->x + tr3->w));
+ tr2 = eina_list_nth(rects, 2);
+ tr3 = eina_list_nth(rects, 3);
+ fail_if((tr2->x != tr3->x));
+
+ EINA_LIST_FREE(rects, tr)
+ free(tr);
+ EINA_LIST_FREE(rects2, tr2)
+ free(tr2);
+ eina_iterator_free(it);
+ eina_iterator_free(it2);
+
+ /* Same run different scripts */
+ evas_object_textblock_text_markup_set(tb, "עבריתenglishрусскийעברית");
+
+ evas_textblock_cursor_pos_set(cur, 3);
+ evas_textblock_cursor_pos_set(main_cur, 7);
+ it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
+ fail_if(!it);
+ rects = eina_iterator_container_get(it);
+ fail_if(!rects);
+
+ fail_if(eina_list_count(rects) != 2);
+
+ EINA_LIST_FREE(rects, tr)
+ free(tr);
+ eina_iterator_free(it);
+
+ /* Same run different styles */
+ evas_object_textblock_text_markup_set(tb, "test<b>test2</b>test3");
+
+ evas_textblock_cursor_pos_set(cur, 3);
+ evas_textblock_cursor_pos_set(main_cur, 11);
+ it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
+ fail_if(!it);
+ rects = eina_iterator_container_get(it);
+
+ fail_if(eina_list_count(rects) != 3);
+
+ EINA_LIST_FREE(rects, tr)
+ free(tr);
+ eina_iterator_free(it);
+
+ /* Bidi text with a few back and forth from bidi. */
+ evas_object_textblock_text_markup_set(tb, "נגכדגךלח eountoheunth ךלחגדךכלח");
+
+ evas_textblock_cursor_pos_set(cur, 0);
+ evas_textblock_cursor_pos_set(main_cur, 28);
+ it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
+ fail_if(!it);
+ rects = eina_iterator_container_get(it);
+ fail_if(!rects);
+
+ ck_assert_int_eq(eina_list_count(rects), 3);
+
+ EINA_LIST_FREE(rects, tr)
+ free(tr);
+ eina_iterator_free(it);
+
END_TB_TEST();
}
END_TEST