#include "evas_common_private.h" #include "evas_private.h" #include "efl_canvas_textblock_internal.h" #define MY_CLASS EFL_TEXT_CURSOR_CLASS #define MY_CLASS_NAME "Efl.Text.Cursor" typedef struct { Efl_Text_Cursor_Handle *handle; Efl_Canvas_Object *text_obj; } Efl_Text_Cursor_Data; struct _Evas_Textblock_Selection_Iterator { Eina_Iterator iterator; /**< Eina Iterator. */ Eina_List *list; /**< Head of list. */ Eina_List *current; /**< Current node in loop. */ }; typedef struct _Evas_Textblock_Selection_Iterator Evas_Textblock_Selection_Iterator; EOLIAN static void _efl_text_cursor_position_set(Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, int position) { evas_textblock_cursor_pos_set(pd->handle, position); } EOLIAN static int _efl_text_cursor_position_get(const Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd) { return evas_textblock_cursor_pos_get(pd->handle); } EOLIAN static Eina_Unicode _efl_text_cursor_content_get(const Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd) { if (pd->handle && pd->handle->node) return eina_ustrbuf_string_get(pd->handle->node->unicode)[pd->handle->pos]; else return 0; } EOLIAN static Eina_Rect _efl_text_cursor_content_geometry_get(const Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd) { Eina_Rect rect = {0}; Eina_Bool item_is = evas_textblock_cursor_format_item_geometry_get(pd->handle, &(rect.x), &(rect.y), &(rect.w), &(rect.h)); if (item_is) return rect; evas_textblock_cursor_pen_geometry_get(pd->handle, &(rect.x), &(rect.y), &(rect.w), &(rect.h)); return rect; } EOLIAN static void _efl_text_cursor_line_number_set(Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, int line_number) { evas_textblock_cursor_line_set(pd->handle, line_number); } EOLIAN static int _efl_text_cursor_line_number_get(const Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd) { Eina_Rect rect = {0}; return evas_textblock_cursor_line_geometry_get(pd->handle, &(rect.x), &(rect.y), &(rect.w), &(rect.h)); } EOLIAN static Eina_Rect _efl_text_cursor_cursor_geometry_get(const Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, Efl_Text_Cursor_Type ctype) { Eina_Rect rc = {0}; Evas_Textblock_Cursor_Type cursor_type = (ctype == EFL_TEXT_CURSOR_TYPE_BEFORE) ? EVAS_TEXTBLOCK_CURSOR_BEFORE : EVAS_TEXTBLOCK_CURSOR_UNDER; evas_textblock_cursor_geometry_bidi_get(pd->handle, &rc.x, &rc.y, &rc.w, &rc.h, NULL, NULL, NULL, NULL, cursor_type); return rc; } EOLIAN static Eina_Bool _efl_text_cursor_lower_cursor_geometry_get(const Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, Eina_Rect *geometry2) { Eina_Rect rc = {0}; Eina_Bool b_ret = EINA_FALSE; b_ret = evas_textblock_cursor_geometry_bidi_get(pd->handle, NULL, NULL, NULL, NULL, &rc.x, &rc.y, &rc.w, &rc.h, EVAS_TEXTBLOCK_CURSOR_BEFORE); if (geometry2) { *geometry2 = rc; } return b_ret; } EOLIAN static Eina_Bool _efl_text_cursor_equal(const Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, const Efl_Text_Cursor *dst) { return evas_textblock_cursor_equal(pd->handle, efl_text_cursor_handle_get(dst)); } EOLIAN static int _efl_text_cursor_compare(const Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, const Efl_Text_Cursor *dst) { return evas_textblock_cursor_compare(pd->handle, efl_text_cursor_handle_get(dst)); } EOLIAN static void _efl_text_cursor_copy(const Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, Efl_Text_Cursor *dst) { Efl_Text_Cursor_Data *pd_dest = efl_data_scope_safe_get(dst, MY_CLASS); EINA_SAFETY_ON_NULL_RETURN(pd_dest); if (!pd->handle) return; Efl_Text_Cursor_Handle *handle = evas_object_textblock_cursor_new(pd->handle->obj); evas_textblock_cursor_copy(pd->handle, handle); pd_dest->text_obj = pd->text_obj; efl_text_cursor_handle_set(dst, handle); evas_textblock_cursor_unref(handle, NULL); } EOLIAN static Efl_Text_Cursor * _efl_text_cursor_efl_duplicate_duplicate(const Eo *obj, Efl_Text_Cursor_Data *pd EINA_UNUSED) { Efl_Text_Cursor *dup = efl_new(MY_CLASS); efl_text_cursor_copy(obj, dup); return dup; } EOLIAN static Eina_Bool _efl_text_cursor_move(Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, Efl_Text_Cursor_Move_Type type) { Eina_Bool moved = EINA_FALSE; int pos = evas_textblock_cursor_pos_get(pd->handle); switch (type) { case EFL_TEXT_CURSOR_MOVE_TYPE_CHAR_NEXT : moved = evas_textblock_cursor_char_next(pd->handle); break; case EFL_TEXT_CURSOR_MOVE_TYPE_CHAR_PREV : moved = evas_textblock_cursor_char_prev(pd->handle); break; case EFL_TEXT_CURSOR_MOVE_TYPE_CLUSTER_NEXT : moved = evas_textblock_cursor_cluster_next(pd->handle); break; case EFL_TEXT_CURSOR_MOVE_TYPE_CLUSTER_PREV : moved = evas_textblock_cursor_cluster_prev(pd->handle); break; case EFL_TEXT_CURSOR_MOVE_TYPE_PARAGRAPH_START : evas_textblock_cursor_paragraph_char_first(pd->handle); if (pos != evas_textblock_cursor_pos_get(pd->handle)) moved = EINA_TRUE; break; case EFL_TEXT_CURSOR_MOVE_TYPE_PARAGRAPH_END : evas_textblock_cursor_paragraph_char_last(pd->handle); if (pos != evas_textblock_cursor_pos_get(pd->handle)) moved = EINA_TRUE; break; case EFL_TEXT_CURSOR_MOVE_TYPE_WORD_START : moved = evas_textblock_cursor_word_start(pd->handle); break; case EFL_TEXT_CURSOR_MOVE_TYPE_WORD_END : moved = evas_textblock_cursor_word_end(pd->handle); break; case EFL_TEXT_CURSOR_MOVE_TYPE_LINE_START : evas_textblock_cursor_line_char_first(pd->handle); if (pos != evas_textblock_cursor_pos_get(pd->handle)) moved = EINA_TRUE; break; case EFL_TEXT_CURSOR_MOVE_TYPE_LINE_END : evas_textblock_cursor_line_char_last(pd->handle); if (pos != evas_textblock_cursor_pos_get(pd->handle)) moved = EINA_TRUE; break; case EFL_TEXT_CURSOR_MOVE_TYPE_FIRST : evas_textblock_cursor_paragraph_first(pd->handle); if (pos != evas_textblock_cursor_pos_get(pd->handle)) moved = EINA_TRUE; break; case EFL_TEXT_CURSOR_MOVE_TYPE_LAST : evas_textblock_cursor_paragraph_last(pd->handle); if (pos != evas_textblock_cursor_pos_get(pd->handle)) moved = EINA_TRUE; break; case EFL_TEXT_CURSOR_MOVE_TYPE_PARAGRAPH_NEXT : moved = evas_textblock_cursor_paragraph_next(pd->handle); break; case EFL_TEXT_CURSOR_MOVE_TYPE_PARAGRAPH_PREV : moved = evas_textblock_cursor_paragraph_prev(pd->handle); break; } return moved; } EOLIAN static void _efl_text_cursor_char_delete(Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd) { evas_textblock_cursor_char_delete(pd->handle); } EOLIAN static Eina_Bool _efl_text_cursor_line_jump_by(Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, int by) { if (!pd->handle) return EINA_FALSE; Eina_Bool moved = EINA_FALSE; int pos = evas_textblock_cursor_pos_get(pd->handle); evas_textblock_cursor_line_jump_by(pd->handle, by); moved = (pos != evas_textblock_cursor_pos_get(pd->handle)); return moved; } EOLIAN static void _efl_text_cursor_char_coord_set(Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, Eina_Position2D coord) { evas_textblock_cursor_char_coord_set(pd->handle, coord.x, coord.y); } EOLIAN static void _efl_text_cursor_cluster_coord_set(Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, Eina_Position2D coord) { evas_textblock_cursor_cluster_coord_set(pd->handle, coord.x, coord.y); } static int _prepend_text_run2(Efl_Text_Cursor_Handle *cur, const char *s, const char *p) { if ((s) && (p > s)) { char *ts; ts = alloca(p - s + 1); strncpy(ts, s, p - s); ts[p - s] = 0; return evas_textblock_cursor_text_prepend(cur, ts); } return 0; } int _cursor_text_append(Efl_Text_Cursor_Handle *cur, const char *text) { if (!text || !cur) return 0; const char *off = text; int len = 0; Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS); evas_object_async_block(obj); while (*off) { char *format = NULL; int n = 1; if (!strncmp(_PARAGRAPH_SEPARATOR_UTF8, off, strlen(_PARAGRAPH_SEPARATOR_UTF8))) { format = "ps"; n = strlen(_PARAGRAPH_SEPARATOR_UTF8); } else if (!strncmp(_NEWLINE_UTF8, off, strlen(_NEWLINE_UTF8))) { format = "br"; n = strlen(_NEWLINE_UTF8); } else if (!strncmp(_TAB_UTF8, off, strlen(_TAB_UTF8))) { format = "tab"; n = strlen(_TAB_UTF8); } if (format) { len += _prepend_text_run2(cur, text, off); if (evas_textblock_cursor_format_prepend(cur, format)) { len++; } text = off + n; /* sync text with next segment */ } off += n; } len += _prepend_text_run2(cur, text, off); return len; } EOLIAN static void _efl_text_cursor_text_insert(Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, const char *text) { _cursor_text_append(pd->handle, text); } EOLIAN static char * _efl_text_cursor_range_text_get(const Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, Efl_Text_Cursor *cur2) { return evas_textblock_cursor_range_text_get(pd->handle, efl_text_cursor_handle_get(cur2), EVAS_TEXTBLOCK_TEXT_PLAIN); } EOLIAN static void _efl_text_cursor_markup_insert(Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, const char *markup) { evas_object_textblock_text_markup_prepend(pd->handle, markup); } EOLIAN static char * _efl_text_cursor_range_markup_get(const Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, Efl_Text_Cursor *cur2) { return evas_textblock_cursor_range_text_get(pd->handle,efl_text_cursor_handle_get(cur2), EVAS_TEXTBLOCK_TEXT_MARKUP); } EOLIAN static Eina_Iterator * _efl_text_cursor_range_geometry_get(Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, Efl_Text_Cursor *cur2) { return evas_textblock_cursor_range_simple_geometry_get(pd->handle, efl_text_cursor_handle_get(cur2)); } /** 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) { Eina_Rectangle *tr; EINA_LIST_FREE(it->list, tr) free(tr); 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. */ static 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; } EOLIAN static Eina_Iterator * _efl_text_cursor_range_precise_geometry_get(Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, Efl_Text_Cursor *cur2) { Eina_List *rects = evas_textblock_cursor_range_geometry_get(pd->handle, efl_text_cursor_handle_get(cur2)); return _evas_textblock_selection_iterator_new(rects); } EOLIAN static void _efl_text_cursor_range_delete(Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd, Efl_Text_Cursor *cur2) { evas_textblock_cursor_range_delete(pd->handle, efl_text_cursor_handle_get(cur2)); } EAPI void efl_text_cursor_handle_set(Eo *obj, Efl_Text_Cursor_Handle *handle) { Efl_Text_Cursor_Data *pd = efl_data_scope_get(obj, MY_CLASS); if (handle == pd->handle) return; Efl_Text_Cursor_Handle *old_handle = pd->handle; pd->handle = evas_textblock_cursor_ref(handle, obj); if (old_handle) { evas_textblock_cursor_unref(old_handle, obj); } } EAPI Efl_Text_Cursor_Handle * efl_text_cursor_handle_get(const Eo *obj) { Efl_Text_Cursor_Data *pd = efl_data_scope_get(obj, MY_CLASS); return pd->handle; } void efl_text_cursor_text_object_set(Eo *cursor, Eo *canvas_text_obj, Eo *text_obj) { Efl_Text_Cursor_Data *pd = efl_data_scope_safe_get(cursor, MY_CLASS); EINA_SAFETY_ON_NULL_RETURN(pd); Efl_Text_Cursor_Handle *handle = NULL; if (efl_isa(canvas_text_obj, EFL_CANVAS_TEXTBLOCK_CLASS)) { pd->text_obj = text_obj; handle = evas_object_textblock_cursor_new(canvas_text_obj); } else { ERR("Expect Canvas Text Object"); } if (handle) { efl_text_cursor_handle_set(cursor, handle); evas_textblock_cursor_unref(handle, NULL); } } EOLIAN static Efl_Canvas_Object * _efl_text_cursor_text_object_get(const Eo *obj EINA_UNUSED, Efl_Text_Cursor_Data *pd) { return pd->text_obj; } EOLIAN static void _efl_text_cursor_efl_object_destructor(Eo *obj, Efl_Text_Cursor_Data *pd) { if (pd->handle) { evas_textblock_cursor_unref(pd->handle, obj); pd->handle = NULL; } if (pd->text_obj) { pd->text_obj = NULL; } efl_destructor(efl_super(obj, MY_CLASS)); } #include "efl_text_cursor.eo.c"