efl/src/lib/evas/canvas/efl_text_cursor.c

479 lines
14 KiB
C

#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)
{
if (!pd->handle) return;
Efl_Text_Cursor_Handle *handle = evas_object_textblock_cursor_new(pd->handle->obj);
evas_textblock_cursor_copy(pd->handle, handle);
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_get(cursor, MY_CLASS);
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"