summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Hacohen <tom@stosb.com>2016-06-08 11:45:40 +0100
committerTom Hacohen <tom@stosb.com>2016-06-16 14:33:45 +0100
commitf85f5cb8ced88c3e269749b4ad5d190de138484a (patch)
treea494ab5444aa53ce1593dfa3b80a1049cd71cde0
parent2ec17cd57df3ea314e5775918d4d1d4b88398786 (diff)
downloadefl-f85f5cb8ced88c3e269749b4ad5d190de138484a.tar.gz
Ui text interactive: introduce this new object (rebase squash)
@feature Ui text: Add a new interactive version of Canvas.Text This object is internal and is essentially a Canvas.Text that accepts key and mouse input. It should be used by Ui.Text as the text layout/input driver. Ui text interactive: Add include guard to header. Ui internal text: Improve input handling support and add selection. Ui internal text: Add a lot of imf stuff. Add more text selection handling code. Add support for allowing selection. Efl.Ui.Text.Interactive: (reword) code format fix. Efl.Ui.Text.Interactive: Remove useless struct members. Add multiline toggle support and a constructor. Text interactive: Add support for legcay newline. Text interactive: Use the new cursor_equal function. Efl.Ui.Text.Interactive: (Rebase split) fixup cursor Efl.Ui.Interactive: (Rebase split) fixup line_jump_by usage Text interactive: Cleanup tab/return handling. Text interactive: Use cursor_equal more. Text interactive: Simplify and unify selection handling in key input. Text interactive: Fix user text change reporting. This is useful for implementing undo/redo. Text interactive: Add documentation to event. Efl text interactive: (Rebase reword) Add a new interface to be used later. Ui text interactive: Migrate one missing change_info call. Ui text interactive: Mark the correct type for the change event. Ui text interactive: (Edited) Move to elementary and add "selection,changed". This is useful for implementing selection handlers. We had to move it to elementary because we started referencing cursors. Ui internal: Emit an event for selection changed. Ui text interactive: Remove unused code. Ui text interactive: Remove more unused code. Ui text interactive: Remove more unused code. Ui text interactive: Rename improperly named function. Ui text interactive: Update code to the new canvas text api. Ui text interactive: provide access to the selection cursors. This is the new API for manipulation selection outside of the object. Just manipulate these cursors to manipulate the selection. Fix previous commit. Ui interactive: Clean up internal functions. Ui interactive: Fix abuse of selection in word deletion. Ui text interactive: Fix selection. Ui text interactive: Fix right key to move next. Ui text interactive: Fix selection using keyboard. Ui text interactive: Don't emit selection changed events twice. We now use the cursor changed event to track changes, so no need to manually emit events ourselves. Ui Text interactive: Remove selection,cleared signal, use changed instead. If the selection cursors are equal, there's no selection, if they are different, there is. Ui text interactive: remove redundant code. Ui text interactive: Don't emit selection changed events on init. Ui text interactive: Remove unused variable. Efl.Ui.Interactive: fixup char_coor usage Ui text interactive: Fix some fixmes. Ui text interactive: fix selection_cursors_get Ui text interactive: Skip key down events if marked ON_HOLD.
-rw-r--r--src/Makefile_Elementary.am15
-rw-r--r--src/lib/elementary/Elementary.h.in1
-rw-r--r--src/lib/elementary/efl_ui_internal_text_interactive.c1398
-rw-r--r--src/lib/elementary/efl_ui_internal_text_interactive.eo14
-rw-r--r--src/lib/elementary/efl_ui_internal_text_interactive.h6
-rw-r--r--src/lib/elementary/efl_ui_text_interactive.eo53
6 files changed, 1486 insertions, 1 deletions
diff --git a/src/Makefile_Elementary.am b/src/Makefile_Elementary.am
index bb5635182f..49e1ea1fb3 100644
--- a/src/Makefile_Elementary.am
+++ b/src/Makefile_Elementary.am
@@ -138,6 +138,12 @@ elm_public_eolian_files = \
lib/elementary/elm_popup_internal_part.eo \
lib/elementary/elm_scroller_internal_part.eo \
lib/elementary/elm_code_widget.eo \
+ lib/elementary/efl_ui_text_interactive.eo \
+ $(NULL)
+
+# Private classes (not exposed or shipped)
+elm_private_eolian_files = \
+ lib/elementary/efl_ui_internal_text_interactive.eo \
$(NULL)
# Legacy classes - not part of public EO API
@@ -156,14 +162,19 @@ elm_public_eolian_h = $(elm_public_eolian_files:%.eo=%.eo.h) \
$(elm_public_eolian_files:%.eo=%.eo.legacy.h) \
$(elm_eolian_type_files:%.eot=%.eot.h)
+elm_private_eolian_c = $(elm_private_eolian_files:%.eo=%.eo.c)
+elm_private_eolian_h = $(elm_private_eolian_files:%.eo=%.eo.h)
+
elm_legacy_eolian_c = $(elm_legacy_eolian_files:%.eo=%.eo.c)
elm_legacy_eolian_eo_h = $(elm_legacy_eolian_files:%.eo=%.eo.h)
elm_legacy_eolian_legacy_h = $(elm_legacy_eolian_files:%.eo=%.eo.legacy.h)
BUILT_SOURCES += \
$(elm_public_eolian_c) \
- $(elm_legacy_eolian_c) \
$(elm_public_eolian_h) \
+ $(elm_private_eolian_c) \
+ $(elm_private_eolian_h) \
+ $(elm_legacy_eolian_c) \
$(elm_legacy_eolian_eo_h) \
$(elm_legacy_eolian_legacy_h)
@@ -366,6 +377,7 @@ includesub_HEADERS = \
lib/elementary/elm_diskselector_eo.h \
lib/elementary/elm_diskselector_legacy.h \
lib/elementary/elm_entry.h \
+ lib/elementary/efl_ui_internal_text_interactive.h \
lib/elementary/elm_entry_common.h \
lib/elementary/elm_entry_eo.h \
lib/elementary/elm_entry_legacy.h \
@@ -594,6 +606,7 @@ lib_elementary_libelementary_la_SOURCES = \
lib/elementary/elm_diskselector.c \
lib/elementary/elm_entry.c \
lib/elementary/efl_ui_flip.c \
+ lib/elementary/efl_ui_internal_text_interactive.c \
lib/elementary/elm_flipselector.c \
lib/elementary/elm_font.c \
lib/elementary/elm_frame.c \
diff --git a/src/lib/elementary/Elementary.h.in b/src/lib/elementary/Elementary.h.in
index dee412d985..e9f1365909 100644
--- a/src/lib/elementary/Elementary.h.in
+++ b/src/lib/elementary/Elementary.h.in
@@ -276,6 +276,7 @@ EAPI extern Elm_Version *elm_version;
# include <efl_ui_image.eo.h>
# include <efl_ui_win.eo.h>
# include <efl_ui_win_standard.eo.h>
+# include <efl_ui_text_interactive.eo.h>
#endif
/* include deprecated calls last of all */
diff --git a/src/lib/elementary/efl_ui_internal_text_interactive.c b/src/lib/elementary/efl_ui_internal_text_interactive.c
new file mode 100644
index 0000000000..4f2a2452cf
--- /dev/null
+++ b/src/lib/elementary/efl_ui_internal_text_interactive.c
@@ -0,0 +1,1398 @@
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+
+#include <Elementary.h>
+#include "efl_ui_internal_text_interactive.h"
+
+#define MY_CLASS EFL_UI_INTERNAL_TEXT_INTERACTIVE_CLASS
+
+#define _PARAGRAPH_SEPARATOR_UTF8 "\xE2\x80\xA9"
+
+
+typedef struct _Efl_Ui_Internal_Text_Interactive_Data
+{
+ Evas_Textblock_Cursor *sel_start, *sel_end;
+ Evas_Textblock_Cursor *preedit_start, *preedit_end;
+ Eina_List *seq;
+ char *selection;
+ Eina_Bool multiline : 1;
+ Eina_Bool composing : 1;
+ Eina_Bool selecting : 1;
+ Eina_Bool have_selection : 1;
+ Eina_Bool select_allow : 1;
+ Eina_Bool had_sel : 1;
+ Eina_Bool input_panel_enable : 1;
+ Eina_Bool prediction_allow : 1;
+ Eina_Bool anchors_updated : 1;
+
+#ifdef HAVE_ECORE_IMF
+ Eina_Bool have_preedit : 1;
+ Eina_Bool commit_cancel : 1; // For skipping useless commit
+ Ecore_IMF_Context *imf_context;
+#endif
+} Efl_Ui_Internal_Text_Interactive_Data;
+
+static void _sel_range_del_emit(Evas_Object *obj, Efl_Ui_Internal_Text_Interactive_Data *en);
+static void _sel_init(Evas_Textblock_Cursor *c, Evas_Object *o, Efl_Ui_Internal_Text_Interactive_Data *en);
+static void _sel_enable(Evas_Textblock_Cursor *c EINA_UNUSED, Evas_Object *o EINA_UNUSED, Efl_Ui_Internal_Text_Interactive_Data *en);
+static void _sel_extend(Evas_Textblock_Cursor *c, Evas_Object *o, Efl_Ui_Internal_Text_Interactive_Data *en);
+static void _sel_clear(Evas_Object *o EINA_UNUSED, Efl_Ui_Internal_Text_Interactive_Data *en);
+static const char *_entry_selection_get(Efl_Ui_Internal_Text_Interactive *obj, Efl_Ui_Internal_Text_Interactive_Data *en);
+static void _entry_imf_cursor_info_set(Evas_Textblock_Cursor *cur, Efl_Ui_Internal_Text_Interactive_Data *en);
+
+#ifdef HAVE_ECORE_IMF
+static void
+_preedit_clear(Efl_Ui_Internal_Text_Interactive_Data *en)
+{
+ if (en->preedit_start)
+ {
+ evas_textblock_cursor_free(en->preedit_start);
+ en->preedit_start = NULL;
+ }
+
+ if (en->preedit_end)
+ {
+ evas_textblock_cursor_free(en->preedit_end);
+ en->preedit_end = NULL;
+ }
+
+ en->have_preedit = EINA_FALSE;
+}
+
+static void
+_preedit_del(Efl_Ui_Internal_Text_Interactive_Data *en)
+{
+ if (!en || !en->have_preedit) return;
+ if (!en->preedit_start || !en->preedit_end) return;
+ if (efl_canvas_text_cursor_equal(en->preedit_start, en->preedit_end)) return;
+
+ /* delete the preedit characters */
+ evas_textblock_cursor_range_delete(en->preedit_start, en->preedit_end);
+}
+
+static Eina_Bool
+_entry_imf_retrieve_surrounding_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, char **text, int *cursor_pos)
+{
+ Efl_Canvas_Text *obj = data;
+ Efl_Canvas_Text_Cursor *cur = efl_canvas_text_cursor_get(obj);
+ const char *str;
+
+ if (text)
+ {
+ str = efl_text_get(obj);
+ if (str)
+ {
+ *text = strdup(str);
+ }
+ else
+ *text = strdup("");
+ }
+
+ if (cursor_pos)
+ {
+ if (cur)
+ *cursor_pos = evas_textblock_cursor_pos_get(cur);
+ else
+ *cursor_pos = 0;
+ }
+
+ return EINA_TRUE;
+}
+
+static void
+_entry_imf_event_commit_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, void *event_info)
+{
+ Efl_Canvas_Text *obj = data;
+ Efl_Ui_Internal_Text_Interactive_Data *en = eo_data_scope_get(obj, MY_CLASS);
+ char *commit_str = event_info;
+
+ if (en->have_selection)
+ {
+ if (strcmp(commit_str, ""))
+ {
+ /* delete selected characters */
+ _sel_range_del_emit(obj, en);
+ _sel_clear(obj, en);
+ }
+ }
+
+ /* delete preedit characters */
+ _preedit_del(en);
+ _preedit_clear(en);
+
+ // Skipping commit process when it is useless
+ if (en->commit_cancel)
+ {
+ en->commit_cancel = EINA_FALSE;
+ return;
+ }
+
+#if 0
+ Edje_Entry_Change_Info *info = NULL;
+ if ((rp->part->entry_mode == EDJE_ENTRY_EDIT_MODE_PASSWORD) &&
+ _password_show_last)
+ _entry_hide_visible_password(en->rp);
+ if ((rp->part->entry_mode == EDJE_ENTRY_EDIT_MODE_PASSWORD) &&
+ _password_show_last && (!en->preedit_start))
+ {
+ info = _text_filter_text_prepend(en, cur, commit_str,
+ "+ password=off", "- password",
+ EINA_TRUE, EINA_TRUE);
+ if (info)
+ {
+ if (en->pw_timer)
+ {
+ ecore_timer_del(en->pw_timer);
+ en->pw_timer = NULL;
+ }
+ if (_password_show_last_timeout >= 0)
+ en->pw_timer = ecore_timer_add
+ (_password_show_last_timeout,
+ _password_timer_cb, en);
+ }
+ }
+ else
+ {
+ info = _text_filter_text_prepend(en, cur, commit_str,
+ NULL, NULL,
+ EINA_TRUE, EINA_TRUE);
+ }
+
+ _entry_imf_cursor_info_set(en);
+ _anchors_get(cur, obj, en);
+ if (info)
+ {
+ _emit("entry,changed", rp->part->name);
+ _emit_full("entry,changed,user", rp->part->name,
+ info, _free_entry_change_info);
+ _emit("cursor,changed", rp->part->name);
+ }
+ _entry_imf_cursor_info_set(en);
+ _entry_real_part_configure(rp);
+#endif
+}
+
+static void
+_entry_imf_event_preedit_changed_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ Efl_Canvas_Text *obj = data;
+ Efl_Canvas_Text_Cursor *cur = efl_canvas_text_cursor_get(obj);
+ Efl_Ui_Internal_Text_Interactive_Data *en = eo_data_scope_get(obj, MY_CLASS);
+ int cursor_pos;
+ int preedit_start_pos, preedit_end_pos;
+ char *preedit_string;
+ char *markup_txt = NULL;
+ char *tagname[] = {
+ NULL, "preedit",
+ // XXX: FIXME: EFL2 - make these 2 preedit_sel's different for efl
+ // 2.0 and beyond. maybe use "preedit_sel", "preedit_hilight",
+ // See https://phab.enlightenment.org/D2980 for this issue
+ "preedit_sel", "preedit_sel",
+ "preedit_sub1", "preedit_sub2", "preedit_sub3", "preedit_sub4"
+ };
+ int i;
+ size_t preedit_type_size = sizeof(tagname) / sizeof(tagname[0]);
+ Eina_Bool preedit_end_state = EINA_FALSE;
+ Eina_List *attrs = NULL, *l = NULL;
+ Ecore_IMF_Preedit_Attr *attr;
+ Eina_Strbuf *buf;
+ Eina_Strbuf *preedit_attr_str;
+
+ if (!en->imf_context) return;
+
+ ecore_imf_context_preedit_string_with_attributes_get(en->imf_context,
+ &preedit_string,
+ &attrs, &cursor_pos);
+ if (!preedit_string) return;
+
+ if (!strcmp(preedit_string, ""))
+ preedit_end_state = EINA_TRUE;
+
+ if (en->have_selection && !preedit_end_state)
+ _sel_range_del_emit(obj, en);
+
+ /* delete preedit characters */
+ _preedit_del(en);
+
+ preedit_start_pos = evas_textblock_cursor_pos_get(cur);
+
+ /* insert preedit character(s) */
+ if (strlen(preedit_string) > 0)
+ {
+ buf = eina_strbuf_new();
+ if (attrs)
+ {
+ EINA_LIST_FOREACH(attrs, l, attr)
+ {
+ if (attr->preedit_type < preedit_type_size &&
+ tagname[attr->preedit_type])
+ {
+ preedit_attr_str = eina_strbuf_new();
+ if (preedit_attr_str)
+ {
+ eina_strbuf_append_n(preedit_attr_str, preedit_string + attr->start_index, attr->end_index - attr->start_index);
+ markup_txt = evas_textblock_text_utf8_to_markup(NULL, eina_strbuf_string_get(preedit_attr_str));
+
+ if (markup_txt)
+ {
+ eina_strbuf_append_printf(buf, "<%s>%s</%s>", tagname[attr->preedit_type], markup_txt, tagname[attr->preedit_type]);
+ free(markup_txt);
+ }
+ eina_strbuf_free(preedit_attr_str);
+ }
+ }
+ else
+ eina_strbuf_append(buf, preedit_string);
+ }
+ }
+ else
+ {
+ eina_strbuf_append(buf, preedit_string);
+ }
+
+ // For skipping useless commit
+ if (!preedit_end_state)
+ en->have_preedit = EINA_TRUE;
+#if 0
+ if ((rp->part->entry_mode == EDJE_ENTRY_EDIT_MODE_PASSWORD) &&
+ _password_show_last)
+ {
+ Edje_Entry_Change_Info *info;
+
+ _entry_hide_visible_password(en->rp);
+ info = _text_filter_markup_prepend(en, cur,
+ eina_strbuf_string_get(buf),
+ "+ password=off",
+ "- password",
+ EINA_TRUE, EINA_TRUE);
+ if (info)
+ {
+ if (en->pw_timer)
+ {
+ ecore_timer_del(en->pw_timer);
+ en->pw_timer = NULL;
+ }
+ if (_password_show_last_timeout >= 0)
+ en->pw_timer = ecore_timer_add
+ (_password_show_last_timeout,
+ _password_timer_cb, en);
+ free(info);
+ }
+ }
+ else
+ _text_filter_markup_prepend(en, cur,
+ eina_strbuf_string_get(buf),
+ NULL, NULL,
+ EINA_TRUE, EINA_FALSE);
+#endif
+ eina_strbuf_free(buf);
+ }
+
+ if (!preedit_end_state)
+ {
+ /* set preedit start cursor */
+ if (!en->preedit_start)
+ en->preedit_start = evas_object_textblock_cursor_new(obj);
+ evas_textblock_cursor_copy(cur, en->preedit_start);
+
+ /* set preedit end cursor */
+ if (!en->preedit_end)
+ en->preedit_end = evas_object_textblock_cursor_new(obj);
+ evas_textblock_cursor_copy(cur, en->preedit_end);
+
+ preedit_end_pos = evas_textblock_cursor_pos_get(cur);
+
+ for (i = 0; i < (preedit_end_pos - preedit_start_pos); i++)
+ {
+ evas_textblock_cursor_char_prev(en->preedit_start);
+ }
+
+ en->have_preedit = EINA_TRUE;
+
+ /* set cursor position */
+ evas_textblock_cursor_pos_set(cur, preedit_start_pos + cursor_pos);
+ }
+
+ _entry_imf_cursor_info_set(cur, en);
+
+ /* delete attribute list */
+ if (attrs)
+ {
+ EINA_LIST_FREE(attrs, attr)
+ free(attr);
+ }
+
+ free(preedit_string);
+}
+
+static void
+_entry_imf_event_delete_surrounding_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, void *event_info)
+{
+ Efl_Canvas_Text *obj = data;
+ Efl_Canvas_Text_Cursor *cur = efl_canvas_text_cursor_get(obj);
+ Efl_Ui_Internal_Text_Interactive_Data *en = eo_data_scope_get(obj, MY_CLASS);
+ Ecore_IMF_Event_Delete_Surrounding *ev = event_info;
+ Evas_Textblock_Cursor *del_start, *del_end;
+ Efl_Ui_Text_Interactive_Change_Info info = {0};
+ int cursor_pos;
+ int start, end;
+
+ cursor_pos = evas_textblock_cursor_pos_get(cur);
+
+ del_start = evas_object_textblock_cursor_new(obj);
+ evas_textblock_cursor_pos_set(del_start, cursor_pos + ev->offset);
+
+ del_end = evas_object_textblock_cursor_new(obj);
+ evas_textblock_cursor_pos_set(del_end, cursor_pos + ev->offset + ev->n_chars);
+
+ start = evas_textblock_cursor_pos_get(del_start);
+ end = evas_textblock_cursor_pos_get(del_end);
+ if (start == end) goto end;
+
+ info.insert = EINA_FALSE;
+ info.position = start;
+ info.length = end - start;
+
+ char *tmp = efl_canvas_text_range_text_get(obj, en->sel_start, en->sel_end);
+
+ eo_event_callback_call(obj, EFL_UI_TEXT_INTERACTIVE_EVENT_CHANGED_USER, &info);
+
+ free(tmp);
+
+ evas_textblock_cursor_range_delete(del_start, del_end);
+
+ _entry_imf_cursor_info_set(cur, en);
+
+end:
+ evas_textblock_cursor_free(del_start);
+ evas_textblock_cursor_free(del_end);
+}
+
+static void
+_entry_imf_event_selection_set_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, void *event_info)
+{
+ Efl_Canvas_Text *obj = data;
+ Efl_Canvas_Text_Cursor *cur = efl_canvas_text_cursor_get(obj);
+ Efl_Ui_Internal_Text_Interactive_Data *en = eo_data_scope_get(obj, MY_CLASS);
+ Ecore_IMF_Event_Selection *ev = event_info;
+
+ if (ev->start == ev->end)
+ {
+ efl_canvas_text_cursor_position_set(cur, ev->start);
+ }
+ else
+ {
+ _sel_clear(obj, en);
+ evas_textblock_cursor_pos_set(cur, ev->start);
+ _sel_enable(cur, obj, en);
+ _sel_init(cur, obj, en);
+ evas_textblock_cursor_pos_set(cur, ev->end);
+ _sel_extend(cur, obj, en);
+ }
+}
+
+static Eina_Bool
+_entry_imf_retrieve_selection_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, char **text)
+{
+ Efl_Canvas_Text *obj = data;
+ Efl_Ui_Internal_Text_Interactive_Data *en = eo_data_scope_get(obj, MY_CLASS);
+ const char *selection_text = NULL;
+
+ if (en->have_selection)
+ {
+ selection_text = _entry_selection_get(obj, en);
+
+ if (text)
+ *text = selection_text ? strdup(selection_text) : NULL;
+
+ return selection_text ? EINA_TRUE : EINA_FALSE;
+ }
+ else
+ return EINA_FALSE;
+}
+
+#endif
+
+static void
+_entry_imf_cursor_location_set(Efl_Canvas_Text_Cursor *cur, Efl_Ui_Internal_Text_Interactive_Data *en)
+{
+#ifdef HAVE_ECORE_IMF
+ Evas_Coord cx = 0, cy = 0, cw = 0, ch = 0;
+ if (!en->imf_context) return;
+
+ efl_canvas_text_cursor_geometry_get(cur, EFL_CANVAS_TEXT_CURSOR_TYPE_BEFORE, &cx, &cy, &cw, &ch, NULL, NULL, NULL, NULL);
+ ecore_imf_context_cursor_location_set(en->imf_context, cx, cy, cw, ch);
+ // FIXME: ecore_imf_context_bidi_direction_set(en->imf_context, (Ecore_IMF_BiDi_Direction)dir);
+#else
+ (void)en;
+#endif
+}
+
+static void
+_entry_imf_cursor_info_set(Evas_Textblock_Cursor *cur, Efl_Ui_Internal_Text_Interactive_Data *en)
+{
+ int cursor_pos;
+
+#ifdef HAVE_ECORE_IMF
+ if (!en->imf_context) return;
+
+ if (en->have_selection)
+ {
+ if (evas_textblock_cursor_compare(en->sel_start, en->sel_end) < 0)
+ cursor_pos = evas_textblock_cursor_pos_get(en->sel_start);
+ else
+ cursor_pos = evas_textblock_cursor_pos_get(en->sel_end);
+ }
+ else
+ cursor_pos = evas_textblock_cursor_pos_get(cur);
+
+ ecore_imf_context_cursor_position_set(en->imf_context, cursor_pos);
+
+ _entry_imf_cursor_location_set(cur, en);
+#else
+ (void)en;
+#endif
+}
+
+static void
+_focus_in_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+#ifdef HAVE_ECORE_IMF
+ Efl_Ui_Internal_Text_Interactive_Data *en = eo_data_scope_get(obj, MY_CLASS);
+ if (!en->imf_context) return;
+
+ ecore_imf_context_focus_in(en->imf_context);
+ _entry_imf_cursor_info_set(obj, en);
+#endif
+}
+
+void
+_entry_imf_context_reset(Efl_Ui_Internal_Text_Interactive_Data *en)
+{
+#ifdef HAVE_ECORE_IMF
+ if (en->imf_context)
+ ecore_imf_context_reset(en->imf_context);
+ if (en->commit_cancel)
+ en->commit_cancel = EINA_FALSE;
+#else
+ (void)en;
+#endif
+}
+
+static void
+_focus_out_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+#ifdef HAVE_ECORE_IMF
+ Efl_Ui_Internal_Text_Interactive_Data *en = eo_data_scope_get(obj, MY_CLASS);
+ if (!en->imf_context) return;
+
+ ecore_imf_context_reset(en->imf_context);
+ ecore_imf_context_focus_out(en->imf_context);
+#endif
+}
+
+static const char *
+_entry_selection_get(Efl_Ui_Internal_Text_Interactive *obj, Efl_Ui_Internal_Text_Interactive_Data *en)
+{
+ if ((!en->selection) && (en->have_selection))
+ en->selection = efl_canvas_text_range_text_get(obj, en->sel_start, en->sel_end);
+ return en->selection;
+}
+
+static Eina_Bool
+_sel_cursor_changed(void *data, const Eo_Event *event EINA_UNUSED)
+{
+ Efl_Canvas_Text_Cursor *obj = data;
+
+ eo_event_callback_call(obj, EFL_UI_TEXT_INTERACTIVE_EVENT_SELECTION_CHANGED, NULL);
+
+ return EO_CALLBACK_CONTINUE;
+}
+
+static void
+_sel_init(Evas_Textblock_Cursor *c, Evas_Object *o, Efl_Ui_Internal_Text_Interactive_Data *en)
+{
+ if (en->have_selection)
+ return;
+
+ eo_event_freeze(o);
+ evas_textblock_cursor_copy(c, en->sel_start);
+ evas_textblock_cursor_copy(c, en->sel_end);
+ eo_event_thaw(o);
+
+ en->have_selection = EINA_FALSE;
+ if (en->selection)
+ {
+ free(en->selection);
+ en->selection = NULL;
+ }
+}
+
+static void
+_sel_enable(Evas_Textblock_Cursor *c EINA_UNUSED,
+ Evas_Object *o EINA_UNUSED, Efl_Ui_Internal_Text_Interactive_Data *en)
+{
+ if (en->have_selection) return;
+ en->have_selection = EINA_TRUE;
+ if (en->selection)
+ {
+ free(en->selection);
+ en->selection = NULL;
+ }
+
+ _entry_imf_context_reset(en);
+}
+
+static void
+_sel_extend(Evas_Textblock_Cursor *c, Evas_Object *o, Efl_Ui_Internal_Text_Interactive_Data *en)
+{
+ if (!en->sel_end) return;
+ _sel_enable(c, o, en);
+ if (efl_canvas_text_cursor_equal(c, en->sel_end)) return;
+
+ evas_textblock_cursor_copy(c, en->sel_end);
+
+ _entry_imf_cursor_info_set(c, en);
+
+ if (en->selection)
+ {
+ free(en->selection);
+ en->selection = NULL;
+ }
+}
+
+static void
+_sel_clear(Evas_Object *o EINA_UNUSED, Efl_Ui_Internal_Text_Interactive_Data *en)
+{
+ en->had_sel = EINA_FALSE;
+ if (en->selection)
+ {
+ free(en->selection);
+ en->selection = NULL;
+ }
+ if (en->have_selection)
+ {
+ en->have_selection = EINA_FALSE;
+
+ efl_canvas_text_cursor_copy(en->sel_start, en->sel_end);
+ }
+}
+
+static void
+_range_del_emit(Evas_Object *obj, Efl_Canvas_Text_Cursor *cur1, Efl_Canvas_Text_Cursor *cur2)
+{
+ size_t start, end;
+ char *tmp;
+ Efl_Ui_Text_Interactive_Change_Info info = {0};
+
+ start = evas_textblock_cursor_pos_get(cur1);
+ end = evas_textblock_cursor_pos_get(cur2);
+ if (start == end)
+ return;
+
+ info.insert = EINA_FALSE;
+ info.position = start;
+ info.length = end - start;
+
+ tmp = efl_canvas_text_range_text_get(obj, cur1, cur2);
+ info.content = tmp;
+
+ eo_event_callback_call(obj, EFL_UI_TEXT_INTERACTIVE_EVENT_CHANGED_USER, &info);
+
+ if (tmp) free(tmp);
+
+ evas_textblock_cursor_range_delete(cur1, cur2);
+}
+
+static void
+_sel_range_del_emit(Evas_Object *obj, Efl_Ui_Internal_Text_Interactive_Data *en)
+{
+ _range_del_emit(obj, en->sel_start, en->sel_end);
+ _sel_clear(obj, en);
+}
+
+static void
+_delete_emit(Evas_Textblock_Cursor *c, Efl_Ui_Internal_Text_Interactive_Data *en EINA_UNUSED, size_t pos)
+{
+ Efl_Ui_Text_Interactive_Change_Info info = {0};
+ Eina_Unicode content[2];
+ content[0] = efl_canvas_text_cursor_content_get(c);
+ content[1] = 0;
+ if (!content[0])
+ return;
+
+ char *tmp = eina_unicode_unicode_to_utf8(content, NULL);
+
+ info.insert = EINA_FALSE;
+ info.position = pos;
+ info.length = 1;
+ info.content = tmp;
+
+ eo_event_callback_call((Eo *) efl_canvas_text_cursor_text_object_get(c),
+ EFL_UI_TEXT_INTERACTIVE_EVENT_CHANGED_USER, &info);
+ if (tmp) free(tmp);
+
+ evas_textblock_cursor_char_delete(c);
+}
+
+static Eina_Bool
+_is_modifier(const char *key)
+{
+ if ((!strncmp(key, "Shift", 5)) ||
+ (!strncmp(key, "Control", 7)) ||
+ (!strncmp(key, "Alt", 3)) ||
+ (!strncmp(key, "Meta", 4)) ||
+ (!strncmp(key, "Super", 5)) ||
+ (!strncmp(key, "Hyper", 5)) ||
+ (!strcmp(key, "Scroll_Lock")) ||
+ (!strcmp(key, "Num_Lock")) ||
+ (!strcmp(key, "Caps_Lock")))
+ return EINA_TRUE;
+ return EINA_FALSE;
+}
+
+static void
+_compose_seq_reset(Efl_Ui_Internal_Text_Interactive_Data *en)
+{
+ char *str;
+
+ EINA_LIST_FREE(en->seq, str)
+ eina_stringshare_del(str);
+ en->composing = EINA_FALSE;
+}
+
+/*
+ * shift: if shift is pressed.
+ * movement_forward: if the movement we are going to do is forward (towards the end of the textblock)
+ */
+static void
+_key_down_sel_pre(Efl_Ui_Internal_Text_Interactive *obj, Efl_Canvas_Text_Cursor *cur, Efl_Ui_Internal_Text_Interactive_Data *en, Eina_Bool shift, Eina_Bool movement_forward)
+{
+ if (en->select_allow)
+ {
+ if (shift)
+ {
+ _sel_init(cur, obj, en);
+ }
+ else if (en->have_selection)
+ {
+ Eina_Bool sel_forward = evas_textblock_cursor_compare(en->sel_start, en->sel_end);
+ if ((sel_forward && movement_forward) || (!sel_forward && !movement_forward))
+ evas_textblock_cursor_copy(en->sel_end, cur);
+ else
+ evas_textblock_cursor_copy(en->sel_start, cur);
+ _sel_clear(obj, en);
+ }
+ }
+}
+
+static void
+_key_down_sel_post(Efl_Ui_Internal_Text_Interactive *obj, Efl_Canvas_Text_Cursor *cur, Efl_Ui_Internal_Text_Interactive_Data *en, Eina_Bool shift)
+{
+ if (en->select_allow)
+ {
+ if (shift) _sel_extend(cur, obj, en);
+ else _sel_clear(obj, en);
+ }
+}
+
+static void
+_key_down_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info)
+{
+ Evas_Event_Key_Down *ev = event_info;
+ Efl_Canvas_Text_Cursor *cur;
+ Eina_Bool control, alt, shift;
+ Eina_Bool multiline;
+ int old_cur_pos;
+ char *string = (char *)ev->string;
+ Eina_Bool free_string = EINA_FALSE;
+ if (!ev->key) return;
+ if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+
+ Efl_Ui_Internal_Text_Interactive_Data *en = eo_data_scope_get(obj, MY_CLASS);
+ cur = efl_canvas_text_cursor_get(obj);
+ old_cur_pos = evas_textblock_cursor_pos_get(cur);
+
+ control = evas_key_modifier_is_set(ev->modifiers, "Control");
+ alt = evas_key_modifier_is_set(ev->modifiers, "Alt");
+ shift = evas_key_modifier_is_set(ev->modifiers, "Shift");
+ multiline = en->multiline;
+
+ /* Translate some keys to strings. */
+ if (!strcmp(ev->key, "Tab"))
+ {
+ if (multiline)
+ {
+ string = "\t";
+ }
+ }
+ else if ((!strcmp(ev->key, "Return")) || (!strcmp(ev->key, "KP_Enter")))
+ {
+ if (multiline)
+ {
+ if (shift || efl_canvas_text_legacy_newline_get(obj))
+ {
+ string = "\n";
+ }
+ else
+ {
+ string = _PARAGRAPH_SEPARATOR_UTF8;
+ }
+ }
+ }
+
+
+ /* Key handling */
+ if (!strcmp(ev->key, "Escape"))
+ {
+ _compose_seq_reset(en);
+ // dead keys here. Escape for now (should emit these)
+ ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+ }
+ else if (!strcmp(ev->key, "Up") ||
+ (!strcmp(ev->key, "KP_Up") && !ev->string))
+ {
+ _compose_seq_reset(en);
+ if (multiline)
+ {
+ _key_down_sel_pre(obj, cur, en, shift, EINA_FALSE);
+
+ efl_canvas_text_cursor_line_jump_by(cur, -1);
+ ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+
+ _key_down_sel_post(obj, cur, en, shift);
+ }
+ }
+ else if (!strcmp(ev->key, "Down") ||
+ (!strcmp(ev->key, "KP_Down") && !ev->string))
+ {
+ _compose_seq_reset(en);
+ if (multiline)
+ {
+ _key_down_sel_pre(obj, cur, en, shift, EINA_TRUE);
+
+ efl_canvas_text_cursor_line_jump_by(cur, 1);
+ ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+
+ _key_down_sel_post(obj, cur, en, shift);
+ }
+ }
+ else if (!strcmp(ev->key, "Left") ||
+ (!strcmp(ev->key, "KP_Left") && !ev->string))
+ {
+ _compose_seq_reset(en);
+ _key_down_sel_pre(obj, cur, en, shift, EINA_FALSE);
+
+ efl_canvas_text_cursor_char_prev(cur);
+ /* If control is pressed, go to the start of the word */
+ if (control) efl_canvas_text_cursor_word_start(cur);
+ ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+
+ _key_down_sel_post(obj, cur, en, shift);
+ }
+ else if (!strcmp(ev->key, "Right") ||
+ (!strcmp(ev->key, "KP_Right") && !ev->string))
+ {
+ _compose_seq_reset(en);
+ _key_down_sel_pre(obj, cur, en, shift, EINA_TRUE);
+
+ /* If control is pressed, go to the end of the word */
+ if (control) efl_canvas_text_cursor_word_end(cur);
+ efl_canvas_text_cursor_char_next(cur);
+ ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+
+ _key_down_sel_post(obj, cur, en, shift);
+ }
+ else if (!strcmp(ev->key, "BackSpace"))
+ {
+ _compose_seq_reset(en);
+ if (control && !en->have_selection)
+ {
+ // del to start of previous word
+ Evas_Textblock_Cursor *tc = evas_object_textblock_cursor_new(obj);
+
+ efl_canvas_text_cursor_copy(tc, cur);
+ evas_textblock_cursor_char_prev(cur);
+ evas_textblock_cursor_word_start(cur);
+
+ _range_del_emit(obj, cur, tc);
+
+ eo_del(tc);
+ }
+ else if ((alt) && (shift))
+ {
+ // undo last action
+ }
+ else
+ {
+ if (en->have_selection)
+ {
+ _sel_range_del_emit(obj, en);
+ }
+ else
+ {
+ if (evas_textblock_cursor_char_prev(cur))
+ {
+ _delete_emit(cur, en, old_cur_pos - 1);
+ }
+ }
+ }
+ _sel_clear(obj, en);
+ ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+ }
+ else if (!strcmp(ev->key, "Delete") ||
+ (!strcmp(ev->key, "KP_Delete") && !ev->string))
+ {
+ _compose_seq_reset(en);
+ if (control)
+ {
+ // del to end of next word
+ Evas_Textblock_Cursor *tc = evas_object_textblock_cursor_new(obj);
+
+ efl_canvas_text_cursor_copy(tc, cur);
+ evas_textblock_cursor_word_end(cur);
+ evas_textblock_cursor_char_next(cur);
+
+ _range_del_emit(obj, cur, tc);
+
+ eo_del(tc);
+ }
+ else if (shift)
+ {
+ // cut
+ }
+ else
+ {
+ if (en->have_selection)
+ {
+ _sel_range_del_emit(obj, en);
+ }
+ else
+ {
+ _delete_emit(cur, en, old_cur_pos);
+ }
+ }
+ _sel_clear(obj, en);
+ ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+ }
+ else if ((!alt) &&
+ (!strcmp(ev->key, "Home") ||
+ ((!strcmp(ev->key, "KP_Home")) && !ev->string)))
+ {
+ _compose_seq_reset(en);
+ _key_down_sel_pre(obj, cur, en, shift, EINA_FALSE);
+
+ if ((control) && (multiline))
+ efl_canvas_text_cursor_paragraph_first(cur);
+ else
+ efl_canvas_text_cursor_line_char_first(cur);
+
+ _key_down_sel_post(obj, cur, en, shift);
+ ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+ }
+ else if ((!alt) &&
+ (!strcmp(ev->key, "End") ||
+ ((!strcmp(ev->key, "KP_End")) && !ev->string)))
+ {
+ _compose_seq_reset(en);
+ _key_down_sel_pre(obj, cur, en, shift, EINA_TRUE);
+
+ if ((control) && (multiline))
+ efl_canvas_text_cursor_paragraph_last(cur);
+ else
+ efl_canvas_text_cursor_line_char_last(cur);
+
+ _key_down_sel_post(obj, cur, en, shift);
+ ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+ }
+ else if (shift && !strcmp(ev->key, "Tab"))
+ {
+ _compose_seq_reset(en);
+ if (multiline)
+ {
+ // remove a tab
+ }
+ }
+ else if ((!strcmp(ev->key, "ISO_Left_Tab")) && (multiline))
+ {
+ _compose_seq_reset(en);
+ // remove a tab
+ ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+ }
+ else if (!strcmp(ev->key, "Prior") ||
+ (!strcmp(ev->key, "KP_Prior") && !ev->string))
+ {
+ _compose_seq_reset(en);
+ _key_down_sel_pre(obj, cur, en, shift, EINA_FALSE);
+
+ efl_canvas_text_cursor_line_jump_by(cur, -10);
+
+ _key_down_sel_post(obj, cur, en, shift);
+ ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+ }
+ else if (!strcmp(ev->key, "Next") ||
+ (!strcmp(ev->key, "KP_Next") && !ev->string))
+ {
+ _compose_seq_reset(en);
+ _key_down_sel_pre(obj, cur, en, shift, EINA_TRUE);
+
+ efl_canvas_text_cursor_line_jump_by(cur, 10);
+
+ _key_down_sel_post(obj, cur, en, shift);
+ ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+ }
+ else
+ {
+ char *compres = NULL;
+ Ecore_Compose_State state;
+
+ if (!en->composing)
+ {
+ _compose_seq_reset(en);
+ en->seq = eina_list_append(en->seq, eina_stringshare_add(ev->key));
+ state = ecore_compose_get(en->seq, &compres);
+ if (state == ECORE_COMPOSE_MIDDLE) en->composing = EINA_TRUE;
+ else en->composing = EINA_FALSE;
+ if (!en->composing)
+ {
+ free(compres);
+ compres = NULL;
+ _compose_seq_reset(en);
+ if (string && (!string[1]) &&
+ (string[0] != 0xa) && (string[0] != 0x9) &&
+ ((string[0] < 0x20) || (string[0] == 0x7f)))
+ goto end;
+ }
+ else
+ {
+ free(compres);
+ compres = NULL;
+ goto end;
+ }
+ }
+ else
+ {
+ if (_is_modifier(ev->key)) goto end;
+ en->seq = eina_list_append(en->seq, eina_stringshare_add(ev->key));
+ state = ecore_compose_get(en->seq, &compres);
+ if (state == ECORE_COMPOSE_NONE)
+ {
+ _compose_seq_reset(en);
+ free(compres);
+ compres = NULL;
+ }
+ else if (state == ECORE_COMPOSE_DONE)
+ {
+ _compose_seq_reset(en);
+ if (compres)
+ {
+ string = compres;
+ free_string = EINA_TRUE;
+ }
+ else free(compres);
+ compres = NULL;
+ }
+ else
+ {
+ free(compres);
+ compres = NULL;
+ goto end;
+ }
+ }
+ if (string)
+ {
+ Efl_Ui_Text_Interactive_Change_Info info = {0};
+ if (en->have_selection)
+ {
+ _sel_range_del_emit(obj, en);
+ info.merge = EINA_TRUE;
+ }
+ info.insert = EINA_TRUE;
+ info.content = string;
+ info.position = efl_canvas_text_cursor_position_get(cur);
+ info.length = eina_unicode_utf8_get_len(string);
+
+ eo_event_callback_call(obj,
+ EFL_UI_TEXT_INTERACTIVE_EVENT_CHANGED_USER, &info);
+
+ efl_canvas_text_cursor_text_insert(cur, string);
+ ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+
+ if (free_string) free(string);
+ }
+ }
+end:
+ (void) 0;
+}
+
+static void
+_cursor_char_coord_set(Efl_Canvas_Text *obj, Efl_Canvas_Text_Cursor *cur, Evas_Coord canvasx, Evas_Coord canvasy, Evas_Coord *_cx, Evas_Coord *_cy)
+{
+ Evas_Coord cx, cy;
+ Evas_Coord x, y, lh = 0, cly = 0;
+ Evas_Textblock_Cursor *line_cur;
+ Evas_Textblock_Cursor *tc;
+
+ tc = evas_object_textblock_cursor_new(obj);
+ evas_textblock_cursor_copy(cur, tc);
+ evas_object_geometry_get(obj, &x, &y, NULL, NULL);
+ cx = canvasx - x;
+ cy = canvasy - y;
+
+ line_cur = evas_object_textblock_cursor_new(obj);
+ evas_textblock_cursor_paragraph_last(line_cur);
+ evas_textblock_cursor_line_geometry_get(line_cur, NULL, &cly, NULL, &lh);
+ /* Consider a threshold of half the line height */
+ if (cy > (cly + lh) && cy < (cly + lh + lh / 2))
+ {
+ cy = cly + lh - 1; // Make it inside Textblock
+ }
+ evas_textblock_cursor_paragraph_first(line_cur);
+ evas_textblock_cursor_line_geometry_get(line_cur, NULL, &cly, NULL, NULL);
+
+ if (cy < cly && cy > (cly - lh / 2))
+ {
+ cy = cly;
+ }
+ evas_textblock_cursor_free(line_cur);
+ /* No need to check return value if not able to set the char coord Textblock
+ * will take care */
+ evas_textblock_cursor_char_coord_set(cur, cx, cy);
+ if (_cx) *_cx = cx;
+ if (_cy) *_cy = cy;
+}
+
+static void
+_mouse_down_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Evas_Coord cx, cy;
+ Evas_Event_Mouse_Down *ev = event_info;
+ Efl_Ui_Internal_Text_Interactive_Data *en = eo_data_scope_get(obj, MY_CLASS);
+ Efl_Canvas_Text_Cursor *cur = efl_canvas_text_cursor_get(obj);
+ Evas_Textblock_Cursor *tc = NULL;
+ Eina_Bool dosel = EINA_FALSE;
+ Eina_Bool shift;
+
+ if ((ev->button != 1) && (ev->button != 2)) return;
+
+#ifdef HAVE_ECORE_IMF
+ if (en->imf_context)
+ {
+ Ecore_IMF_Event_Mouse_Down ecore_ev;
+ // ecore_imf_evas_event_mouse_down_wrap(ev, &ecore_ev);
+ if (ecore_imf_context_filter_event(en->imf_context,
+ ECORE_IMF_EVENT_MOUSE_DOWN,
+ (Ecore_IMF_Event *)&ecore_ev))
+ return;
+ }
+#endif
+
+ _entry_imf_context_reset(en);
+
+ shift = evas_key_modifier_is_set(ev->modifiers, "Shift");
+
+ if (en->select_allow && ev->button != 2) dosel = EINA_TRUE;
+ if (dosel)
+ {
+ if (ev->flags & EVAS_BUTTON_TRIPLE_CLICK)
+ {
+ if (shift)
+ {
+ tc = evas_object_textblock_cursor_new(obj);
+ evas_textblock_cursor_copy(cur, tc);
+ if (evas_textblock_cursor_compare(cur, en->sel_start) < 0)
+ evas_textblock_cursor_line_char_first(cur);
+ else
+ evas_textblock_cursor_line_char_last(cur);
+ _sel_extend(cur, obj, en);
+ }
+ else
+ {
+ en->have_selection = EINA_FALSE;
+ en->selecting = EINA_FALSE;
+ _sel_clear(obj, en);
+ tc = evas_object_textblock_cursor_new(obj);
+ evas_textblock_cursor_copy(cur, tc);
+ evas_textblock_cursor_line_char_first(cur);
+ _sel_init(cur, obj, en);
+ evas_textblock_cursor_line_char_last(cur);
+ _sel_extend(cur, obj, en);
+ }
+ goto end;
+ }
+ else if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK)
+ {
+ if (shift)
+ {
+ tc = evas_object_textblock_cursor_new(obj);
+ evas_textblock_cursor_copy(cur, tc);
+ if (evas_textblock_cursor_compare(cur, en->sel_start) < 0)
+ evas_textblock_cursor_word_start(cur);
+ else
+ {
+ evas_textblock_cursor_word_end(cur);
+ evas_textblock_cursor_char_next(cur);
+ }
+ _sel_extend(cur, obj, en);
+ }
+ else
+ {
+ en->have_selection = EINA_FALSE;
+ en->selecting = EINA_FALSE;
+ _sel_clear(obj, en);
+ tc = evas_object_textblock_cursor_new(obj);
+ evas_textblock_cursor_copy(cur, tc);
+ evas_textblock_cursor_word_start(cur);
+ _sel_init(cur, obj, en);
+ evas_textblock_cursor_word_end(cur);
+ evas_textblock_cursor_char_next(cur);
+ _sel_extend(cur, obj, en);
+ }
+ goto end;
+ }
+ }
+ _cursor_char_coord_set(obj, cur, ev->canvas.x, ev->canvas.y, &cx, &cy);
+
+ if (dosel)
+ {
+ if ((en->have_selection) && (shift))
+ {
+ _sel_extend(cur, obj, en);
+ }
+ else
+ {
+ en->selecting = EINA_TRUE;
+ _sel_clear(obj, en);
+ _sel_init(cur, obj, en);
+ }
+ }
+
+ if (ev->button == 2)
+ {
+ }
+end:
+ (void) 0;
+}
+
+static void
+_mouse_up_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info)
+{
+ Evas_Coord cx, cy;
+ Efl_Ui_Internal_Text_Interactive_Data *en = eo_data_scope_get(obj, MY_CLASS);
+ Efl_Canvas_Text_Cursor *cur = efl_canvas_text_cursor_get(obj);
+ Evas_Event_Mouse_Up *ev = event_info;
+
+ if ((!ev) || (ev->button != 1)) return;
+
+ /* We don't check for ON_HOLD because we'd like to end selection anyway when
+ * mouse is up, even if it's held. */
+
+#ifdef HAVE_ECORE_IMF
+ if (en->imf_context)
+ {
+ Ecore_IMF_Event_Mouse_Up ecore_ev;
+// ecore_imf_evas_event_mouse_up_wrap(ev, &ecore_ev);
+ if (ecore_imf_context_filter_event(en->imf_context,
+ ECORE_IMF_EVENT_MOUSE_UP,
+ (Ecore_IMF_Event *)&ecore_ev))
+ return;
+ }
+#endif
+
+ _cursor_char_coord_set(obj, cur, ev->canvas.x, ev->canvas.y, &cx, &cy);
+
+ if (en->select_allow)
+ {
+ efl_canvas_text_cursor_copy(en->sel_end, cur);
+ }
+ if (en->selecting)
+ {
+ if (en->have_selection)
+ en->had_sel = EINA_TRUE;
+ en->selecting = EINA_FALSE;
+ }
+
+ _entry_imf_cursor_info_set(cur, en);
+}
+
+static void
+_mouse_move_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info)
+{
+ Evas_Coord cx, cy;
+ Efl_Ui_Internal_Text_Interactive_Data *en = eo_data_scope_get(obj, MY_CLASS);
+ Efl_Canvas_Text_Cursor *cur = efl_canvas_text_cursor_get(obj);
+ Evas_Event_Mouse_Move *ev = event_info;
+ Evas_Coord x, y, w, h;
+ Evas_Textblock_Cursor *tc;
+
+#ifdef HAVE_ECORE_IMF
+ if (en->imf_context)
+ {
+ Ecore_IMF_Event_Mouse_Move ecore_ev;
+// ecore_imf_evas_event_mouse_move_wrap(ev, &ecore_ev);
+ if (ecore_imf_context_filter_event(en->imf_context,
+ ECORE_IMF_EVENT_MOUSE_MOVE,
+ (Ecore_IMF_Event *)&ecore_ev))
+ return;
+ }
+#endif
+
+ if (en->selecting)
+ {
+ tc = evas_object_textblock_cursor_new(obj);
+ evas_textblock_cursor_copy(cur, tc);
+ evas_object_geometry_get(obj, &x, &y, &w, &h);
+ cx = ev->cur.canvas.x - x;
+ cy = ev->cur.canvas.y - y;
+
+ if (en->multiline)
+ {
+ efl_canvas_text_cursor_coord_set(cur, cx, cy);
+ }
+ else
+ {
+ Evas_Coord lx, ly, lw, lh;
+ evas_textblock_cursor_paragraph_first(cur);
+ evas_textblock_cursor_line_geometry_get(cur, &lx, &ly, &lw, &lh);
+ efl_canvas_text_cursor_coord_set(cur, cx, ly + (lh / 2));
+ }
+
+ if (en->select_allow)
+ {
+ _sel_extend(cur, obj, en);
+
+ if (!efl_canvas_text_cursor_equal(en->sel_start, en->sel_end))
+ _sel_enable(cur, obj, en);
+ }
+ evas_textblock_cursor_free(tc);
+ }
+}
+
+EOLIAN static Eo_Base *
+_efl_ui_internal_text_interactive_eo_base_constructor(Eo *obj, Efl_Ui_Internal_Text_Interactive_Data *en)
+{
+ en->select_allow = EINA_TRUE;
+ en->multiline = EINA_TRUE;
+ return eo_constructor(eo_super(obj, MY_CLASS));
+}
+
+EOLIAN static Eo_Base *
+_efl_ui_internal_text_interactive_eo_base_finalize(Eo *obj, Efl_Ui_Internal_Text_Interactive_Data *en)
+{
+ evas_object_event_callback_add(obj, EVAS_CALLBACK_FOCUS_IN, _focus_in_cb, NULL);
+ evas_object_event_callback_add(obj, EVAS_CALLBACK_FOCUS_OUT, _focus_out_cb, NULL);
+ evas_object_event_callback_add(obj, EVAS_CALLBACK_KEY_DOWN, _key_down_cb, NULL);
+ evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, NULL);
+ evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, NULL);
+ evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_MOVE, _mouse_move_cb, NULL);
+
+ en->sel_start = evas_object_textblock_cursor_new(obj);
+ en->sel_end = evas_object_textblock_cursor_new(obj);
+
+ eo_event_callback_add(en->sel_start, EFL_CANVAS_TEXT_CURSOR_EVENT_CHANGED,
+ _sel_cursor_changed, obj);
+ eo_event_callback_add(en->sel_end, EFL_CANVAS_TEXT_CURSOR_EVENT_CHANGED,
+ _sel_cursor_changed, obj);
+
+ en->input_panel_enable = EINA_TRUE;
+
+#ifdef HAVE_ECORE_IMF
+ {
+ const char *ctx_id;
+ const Ecore_IMF_Context_Info *ctx_info;
+ Evas *evas = evas_common_evas_get(obj);
+ // _need_imf();
+
+ en->commit_cancel = EINA_FALSE;
+
+ ctx_id = ecore_imf_context_default_id_get();
+ if (ctx_id)
+ {
+ ctx_info = ecore_imf_context_info_by_id_get(ctx_id);
+ if (!ctx_info->canvas_type ||
+ strcmp(ctx_info->canvas_type, "evas") == 0)
+ {
+ en->imf_context = ecore_imf_context_add(ctx_id);
+ }
+ else
+ {
+ ctx_id = ecore_imf_context_default_id_by_canvas_type_get("evas");
+ if (ctx_id)
+ {
+ en->imf_context = ecore_imf_context_add(ctx_id);
+ }
+ }
+ }
+ else
+ en->imf_context = NULL;
+
+ if (!en->imf_context) goto done;
+
+ ecore_imf_context_client_window_set
+ (en->imf_context,
+ (void *)ecore_evas_window_get
+ (ecore_evas_ecore_evas_get(evas)));
+ ecore_imf_context_client_canvas_set(en->imf_context, evas);
+
+ ecore_imf_context_retrieve_surrounding_callback_set(en->imf_context,
+ _entry_imf_retrieve_surrounding_cb, obj);
+ ecore_imf_context_retrieve_selection_callback_set(en->imf_context, _entry_imf_retrieve_selection_cb, obj);
+ ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_COMMIT, _entry_imf_event_commit_cb, obj);
+ ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, _entry_imf_event_delete_surrounding_cb, obj);
+ ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, _entry_imf_event_preedit_changed_cb, obj);
+ ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_SELECTION_SET, _entry_imf_event_selection_set_cb, obj);
+#if 0
+ // FIXME
+ ecore_imf_context_input_mode_set(en->imf_context,
+ rp->part->entry_mode == EDJE_ENTRY_EDIT_MODE_PASSWORD ?
+ ECORE_IMF_INPUT_MODE_INVISIBLE : ECORE_IMF_INPUT_MODE_FULL);
+
+ if (rp->part->entry_mode == EDJE_ENTRY_EDIT_MODE_PASSWORD)
+ ecore_imf_context_input_panel_language_set(en->imf_context, ECORE_IMF_INPUT_PANEL_LANG_ALPHABET);
+#endif
+
+ if (en->multiline)
+ ecore_imf_context_input_hint_set(en->imf_context,
+ ecore_imf_context_input_hint_get(en->imf_context) | ECORE_IMF_INPUT_HINT_MULTILINE);
+ }
+#endif
+
+done:
+ return eo_finalize(eo_super(obj, MY_CLASS));
+}
+
+EOLIAN static void
+_efl_ui_internal_text_interactive_efl_ui_text_interactive_selection_allowed_set(Eo *obj EINA_UNUSED, Efl_Ui_Internal_Text_Interactive_Data *pd, Eina_Bool allowed)
+{
+ pd->select_allow = allowed;
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_internal_text_interactive_efl_ui_text_interactive_selection_allowed_get(Eo *obj EINA_UNUSED, Efl_Ui_Internal_Text_Interactive_Data *pd)
+{
+ return pd->select_allow;
+}
+
+EOLIAN static void
+_efl_ui_internal_text_interactive_efl_ui_text_interactive_selection_cursors_get(Eo *obj EINA_UNUSED, Efl_Ui_Internal_Text_Interactive_Data *pd, Efl_Canvas_Text_Cursor **start, Evas_Textblock_Cursor **end)
+{
+ if (start) *start = pd->sel_start;
+ if (end) *end = pd->sel_end;
+}
+
+EOLIAN static void
+_efl_ui_internal_text_interactive_efl_ui_text_interactive_multiline_set(Eo *obj EINA_UNUSED, Efl_Ui_Internal_Text_Interactive_Data *pd, Eina_Bool enabled)
+{
+ pd->multiline = enabled;
+}
+
+EOLIAN static Eina_Bool
+_efl_ui_internal_text_interactive_efl_ui_text_interactive_multiline_get(Eo *obj EINA_UNUSED, Efl_Ui_Internal_Text_Interactive_Data *pd)
+{
+ return pd->multiline;
+}
+
+
+#include "efl_ui_internal_text_interactive.eo.c"
+#include "efl_ui_text_interactive.eo.c"
diff --git a/src/lib/elementary/efl_ui_internal_text_interactive.eo b/src/lib/elementary/efl_ui_internal_text_interactive.eo
new file mode 100644
index 0000000000..6c660fe049
--- /dev/null
+++ b/src/lib/elementary/efl_ui_internal_text_interactive.eo
@@ -0,0 +1,14 @@
+class Efl.Ui.Internal.Text.Interactive (Efl.Canvas.Text, Efl.Ui.Text.Interactive)
+{
+ [[An internal object that is in charge of the interactive aspect of the text widget
+
+ This object is in charge of processing input, moving the text caret and etc.
+ ]]
+ implements {
+ Eo.Base.constructor;
+ Eo.Base.finalize;
+ Efl.Ui.Text.Interactive.selection_allowed;
+ Efl.Ui.Text.Interactive.selection_cursors.get;
+ Efl.Ui.Text.Interactive.multiline;
+ }
+}
diff --git a/src/lib/elementary/efl_ui_internal_text_interactive.h b/src/lib/elementary/efl_ui_internal_text_interactive.h
new file mode 100644
index 0000000000..e3a5071b04
--- /dev/null
+++ b/src/lib/elementary/efl_ui_internal_text_interactive.h
@@ -0,0 +1,6 @@
+#ifndef _EFL_UI_INTERNAL_TEXT_INTERACTIVE_H
+#define _EFL_UI_INTERNAL_TEXT_INTERACTIVE_H
+
+#include "efl_ui_internal_text_interactive.eo.h"
+
+#endif
diff --git a/src/lib/elementary/efl_ui_text_interactive.eo b/src/lib/elementary/efl_ui_text_interactive.eo
new file mode 100644
index 0000000000..583d8418a4
--- /dev/null
+++ b/src/lib/elementary/efl_ui_text_interactive.eo
@@ -0,0 +1,53 @@
+struct Efl.Ui.Text.Interactive.Change_Info {
+ [[This structure includes all the information about content changes.
+
+ It's meant to be used to implement undo/redo.
+ ]]
+ content: string; [[The content added/removed]]
+ position: size; [[The position where it was added/removed]]
+ length: size; [[The length of content in characters (not bytes, actual unicode characters)]]
+ insert: bool; [[$true if the content was inserted, $false if removei]]
+ merge: bool; [[$true if can be merged with the previous one. Used for example with insertion when something is already selected]]
+}
+
+interface Efl.Ui.Text.Interactive ()
+{
+ [[This is an interface interactive text inputs should implement]]
+ methods {
+ @property selection_allowed {
+ [[Whether or not selection is allowed on this object]]
+ set {}
+ get {}
+ values {
+ allowed: bool; [[$true if enabled]]
+ }
+ }
+ @property selection_cursors {
+ [[The cursors used for selection handling.
+
+ If the cursors are equal (@Efl.Canvas.Text.Cursor.equal), there is
+ no selection.
+
+ You are allowed to retain and modify them. Modifying them modifies
+ the selection of the object.
+ ]]
+ get {}
+ values {
+ start: Efl.Canvas.Text.Cursor; [[The start of the selection]]
+ end: Efl.Canvas.Text.Cursor; [[The end of the selection]]
+ }
+ }
+ @property multiline {
+ [[Whether or not this textblock is allowed to be multiline]]
+ set {}
+ get {}
+ values {
+ enabled: bool; [[$true if enabled]]
+ }
+ }
+ }
+ events {
+ changed,user: Efl.Ui.Text.Interactive.Change_Info; [[The text object has changed due to user interaction]]
+ selection,changed; [[The selection on the object has changed. Query using @.selection_cursors]]
+ }
+}