/** * Ecore example illustrating how to use ecore imf. * * @verbatim * gcc -o ecore_imf_example ecore_imf_example.c `pkg-config --cflags --libs ecore evas eina ecore-evas ecore-imf ecore-imf-evas` * @endverbatim */ #include #include #include #include #include #include #define WIDTH 480 #define HEIGHT 800 typedef struct _Entry Entry; struct _Entry { Evas_Object *rect; Evas_Object *txt_obj; Evas_Textblock_Style *txt_style; Evas_Textblock_Cursor *cursor; Evas_Textblock_Cursor *preedit_start; Evas_Textblock_Cursor *preedit_end; Ecore_IMF_Context *imf_context; Eina_Bool have_preedit : 1; }; static void _imf_cursor_info_set(Entry *en); static void _mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info) { Entry *en = data; Evas_Event_Mouse_Down *ev = event_info; if (!en) return; 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; // ecore_imf_context_reset should be called before calculating new cursor position ecore_imf_context_reset(en->imf_context); } // calculate new cursor position } static void _mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info) { Entry *en = data; Evas_Event_Mouse_Up *ev = event_info; if (!en) return; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) { _imf_cursor_info_set(en); return; } 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; } if (en->rect) { if (evas_object_focus_get(en->rect)) { // notify cursor information _imf_cursor_info_set(en); } else evas_object_focus_set(en->rect, EINA_TRUE); } } static void _entry_focus_in_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info EINA_UNUSED) { Entry *en = data; if (!en) return; if (en->imf_context) ecore_imf_context_focus_in(en->imf_context); // notify the cursor information _imf_cursor_info_set(en); } static void _entry_focus_out_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info EINA_UNUSED) { Entry *en = data; if (!en) return; if (en->imf_context) { // ecore_imf_context_reset should be called for flushing the preedit string in focus-out event handler ecore_imf_context_reset(en->imf_context); ecore_imf_context_focus_out(en->imf_context); } } static void _canvas_focus_in_cb(void *data EINA_UNUSED, Evas *e, void *event_info EINA_UNUSED) { Entry *en; Evas_Object *obj = evas_focus_get(e); if (!obj) return; en = evas_object_data_get(obj, "Entry"); if (en) _entry_focus_in_cb(en, NULL, NULL, NULL); } static void _canvas_focus_out_cb(void *data EINA_UNUSED, Evas *e, void *event_info EINA_UNUSED) { Entry *en; Evas_Object *obj = evas_focus_get(e); if (!obj) return; en = evas_object_data_get(obj, "Entry"); if (en) _entry_focus_out_cb(en, NULL, NULL, NULL); } static void _imf_cursor_info_set(Entry *en) { Evas_Coord x, y, w, h; Evas_Coord cx, cy, cw, ch; // cursor geometry int cursor_pos; // cursor position in chars (Not bytes) Evas_BiDi_Direction dir; if (!en) return; // get cursor geometry if (en->txt_obj) evas_object_geometry_get(en->txt_obj, &x, &y, &w, &h); if (en->cursor && en->imf_context) { evas_textblock_cursor_geometry_get(en->cursor, &cx, &cy, &cw, &ch, &dir, EVAS_TEXTBLOCK_CURSOR_BEFORE); // get cursor position cursor_pos = evas_textblock_cursor_pos_get(en->cursor); ecore_imf_context_cursor_position_set(en->imf_context, cursor_pos); ecore_imf_context_cursor_location_set(en->imf_context, x + cx, y + cy, cw, ch); ecore_imf_context_bidi_direction_set(en->imf_context, (Ecore_IMF_BiDi_Direction)dir); } } static void _preedit_del(Entry *en) { if (!en || !en->have_preedit) return; if (!en->preedit_start || !en->preedit_end) return; if (!evas_textblock_cursor_compare(en->preedit_start, en->preedit_end)) return; // delete the preedit characters evas_textblock_cursor_range_delete(en->preedit_start, en->preedit_end); } static void _preedit_clear(Entry *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 Eina_Bool _ecore_imf_retrieve_surrounding_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, char **text, int *cursor_pos) { // This callback will be called when the Input Method Context module requests the surrounding context. Entry *en = data; const char *str; if (!en) return EINA_FALSE; str = evas_object_textblock_text_markup_get(en->txt_obj); if (text) *text = str ? strdup(str) : strdup(""); // get the current position of cursor if (cursor_pos && en->cursor) *cursor_pos = evas_textblock_cursor_pos_get(en->cursor); return EINA_TRUE; } static void _ecore_imf_event_delete_surrounding_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, void *event_info) { // called when the input method needs to delete all or part of the context surrounding the cursor Entry *en = data; Ecore_IMF_Event_Delete_Surrounding *ev = event_info; Evas_Textblock_Cursor *del_start, *del_end; int cursor_pos; if ((!en) || (!ev) || (!en->cursor)) return; // get the current cursor position cursor_pos = evas_textblock_cursor_pos_get(en->cursor); // start cursor position to be deleted del_start = evas_object_textblock_cursor_new(en->txt_obj); evas_textblock_cursor_pos_set(del_start, cursor_pos + ev->offset); // end cursor position to be deleted del_end = evas_object_textblock_cursor_new(en->txt_obj); evas_textblock_cursor_pos_set(del_end, cursor_pos + ev->offset + ev->n_chars); // implement function to delete character(s) from 'cursor_pos+ev->offset' cursor position to 'cursor_pos + ev->offset + ev->n_chars' evas_textblock_cursor_range_delete(del_start, del_end); evas_textblock_cursor_free(del_start); evas_textblock_cursor_free(del_end); } static void _ecore_imf_event_commit_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, void *event_info) { Entry *en = data; char *commit_str = (char *)event_info; if (!en) return; // delete preedit string _preedit_del(en); _preedit_clear(en); printf("commit string : %s\n", commit_str); // insert the commit string in the editor if (en->cursor && commit_str) evas_object_textblock_text_markup_prepend(en->cursor, commit_str); // notify the cursor information _imf_cursor_info_set(en); return; } static void _ecore_imf_event_preedit_changed_cb(void *data, Ecore_IMF_Context *ctx, void *event_info EINA_UNUSED) { // example how to get preedit string Entry *en = data; char *preedit_string; int cursor_pos; Eina_List *attrs = NULL; Eina_List *l; Ecore_IMF_Preedit_Attr *attr; Ecore_IMF_Context *imf_context = ctx; int preedit_start_pos, preedit_end_pos; int i; Eina_Bool preedit_end_state = EINA_FALSE; if (!en || !en->cursor) return; // get preedit string and attributes ecore_imf_context_preedit_string_with_attributes_get(imf_context, &preedit_string, &attrs, &cursor_pos); printf("preedit string : %s\n", preedit_string); if (!strcmp(preedit_string, "")) preedit_end_state = EINA_TRUE; // delete preedit _preedit_del(en); preedit_start_pos = evas_textblock_cursor_pos_get(en->cursor); // insert preedit character(s) if (strlen(preedit_string) > 0) { if (attrs) { EINA_LIST_FOREACH(attrs, l, attr) { if (attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB1) // style type { // apply appropriate style such as underline } else if (attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB2 || attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB3) { // apply appropriate style such as underline } } // insert code to display preedit string in your editor evas_object_textblock_text_markup_prepend(en->cursor, preedit_string); } } if (!preedit_end_state) { // set preedit start cursor if (!en->preedit_start) en->preedit_start = evas_object_textblock_cursor_new(en->txt_obj); evas_textblock_cursor_copy(en->cursor, en->preedit_start); // set preedit end cursor if (!en->preedit_end) en->preedit_end = evas_object_textblock_cursor_new(en->txt_obj); evas_textblock_cursor_copy(en->cursor, en->preedit_end); preedit_end_pos = evas_textblock_cursor_pos_get(en->cursor); 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(en->cursor, preedit_start_pos + cursor_pos); } // notify the cursor information _imf_cursor_info_set(en); EINA_LIST_FREE(attrs, attr) free(attr); free(preedit_string); } static void _key_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info) { Entry *en = data; Evas_Event_Key_Down *ev = event_info; Eina_Bool control, alt, shift; if ((!en) || (!ev->key) || (!en->cursor)) return; if (en->imf_context) { Ecore_IMF_Event_Key_Down ecore_ev; ecore_imf_evas_event_key_down_wrap(ev, &ecore_ev); if (ecore_imf_context_filter_event(en->imf_context, ECORE_IMF_EVENT_KEY_DOWN, (Ecore_IMF_Event *)&ecore_ev)) return; } 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"); (void)alt; (void)shift; if (!strcmp(ev->key, "BackSpace")) { if (evas_textblock_cursor_char_prev(en->cursor)) { evas_textblock_cursor_char_delete(en->cursor); // notify the cursor information _imf_cursor_info_set(en); } return; } else if (!strcmp(ev->key, "Delete") || (!strcmp(ev->key, "KP_Delete") && !ev->string)) { // FILLME } else if ((control) && (!strcmp(ev->key, "v"))) { // ctrl + v // FILLME } else if ((control) && (!strcmp(ev->key, "a"))) { // ctrl + a // FILLME } else if ((control) && (!strcmp(ev->key, "A"))) { // ctrl + A // FILLME } else if ((control) && ((!strcmp(ev->key, "c") || (!strcmp(ev->key, "Insert"))))) { // ctrl + c // FILLME } else if ((control) && ((!strcmp(ev->key, "x") || (!strcmp(ev->key, "m"))))) { // ctrl + x // FILLME } else if ((control) && (!strcmp(ev->key, "z"))) { // ctrl + z (undo) // FILLME } else if ((control) && (!strcmp(ev->key, "y"))) { // ctrl + y (redo) // FILLME } else if ((!strcmp(ev->key, "Return")) || (!strcmp(ev->key, "KP_Enter"))) { // FILLME } else { if (ev->string) { printf("key down string : %s\n", ev->string); evas_object_textblock_text_markup_prepend(en->cursor, ev->string); } } // notify the cursor information _imf_cursor_info_set(en); } static void _key_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info) { Entry *en = data; Evas_Event_Key_Up *ev = event_info; if (!en) return; if (en->imf_context) { Ecore_IMF_Event_Key_Up ecore_ev; ecore_imf_evas_event_key_up_wrap(ev, &ecore_ev); if (ecore_imf_context_filter_event(en->imf_context, ECORE_IMF_EVENT_KEY_UP, (Ecore_IMF_Event *)&ecore_ev)) return; } } static void create_input_field(Evas *evas, Entry *en, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h) { if (!en) return; en->have_preedit = EINA_FALSE; en->preedit_start = NULL; en->preedit_end = NULL; // create the background for text input field en->rect = evas_object_rectangle_add(evas); evas_object_color_set(en->rect, 150, 150, 150, 255); // gray color evas_object_move(en->rect, x, y); evas_object_resize(en->rect, w, h); evas_object_show(en->rect); evas_object_data_set(en->rect, "Entry", en); // create text object for displaying text en->txt_obj = evas_object_textblock_add(evas); evas_object_color_set(en->txt_obj, 0, 0, 0, 255); evas_object_pass_events_set(en->txt_obj, EINA_TRUE); evas_object_move(en->txt_obj, x, y); evas_object_resize(en->txt_obj, w, h); evas_object_show(en->txt_obj); // set style on textblock static const char *style_buf = "DEFAULT='font=Sans font_size=30 color=#000 text_class=entry'" "newline='br'" "b='+ font=Sans:style=bold'"; en->txt_style = evas_textblock_style_new(); evas_textblock_style_set(en->txt_style, style_buf); evas_object_textblock_style_set(en->txt_obj, en->txt_style); // create cursor en->cursor = evas_object_textblock_cursor_new(en->txt_obj); // create input context const char *default_id = ecore_imf_context_default_id_get(); if (!default_id) { fprintf(stderr, "Can't create ecore_imf_context\n"); return; } en->imf_context = ecore_imf_context_add(default_id); ecore_imf_context_client_canvas_set(en->imf_context, evas); // register key event handler evas_object_event_callback_add(en->rect, EVAS_CALLBACK_KEY_DOWN, _key_down_cb, en); evas_object_event_callback_add(en->rect, EVAS_CALLBACK_KEY_UP, _key_up_cb, en); // register mouse event handler evas_object_event_callback_add(en->rect, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, en); evas_object_event_callback_add(en->rect, EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, en); // register focus event handler evas_object_event_callback_add(en->rect, EVAS_CALLBACK_FOCUS_IN, _entry_focus_in_cb, en); evas_object_event_callback_add(en->rect, EVAS_CALLBACK_FOCUS_OUT, _entry_focus_out_cb, en); // register retrieve surrounding callback ecore_imf_context_retrieve_surrounding_callback_set(en->imf_context, _ecore_imf_retrieve_surrounding_cb, en); // register commit event callback ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_COMMIT, _ecore_imf_event_commit_cb, en); // register preedit changed event handler ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, _ecore_imf_event_preedit_changed_cb, en); // register surrounding delete event callback ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, _ecore_imf_event_delete_surrounding_cb, en); } static void delete_input_field(Entry *en) { if (!en) return; if (en->rect) { evas_object_del(en->rect); en->rect = NULL; } if (en->cursor) { evas_textblock_cursor_free(en->cursor); en->cursor = NULL; } 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; } if (en->txt_obj) { evas_object_del(en->txt_obj); en->txt_obj = NULL; } if (en->txt_style) { evas_textblock_style_free(en->txt_style); en->txt_style = NULL; } if (en->imf_context) { ecore_imf_context_del(en->imf_context); en->imf_context = NULL; } } int main(void) { Ecore_Evas *ee; Evas *evas; Entry en1, en2; if (!ecore_evas_init()) { fprintf(stderr, "failed to call ecore_evas_init()\n"); return EXIT_FAILURE; } ecore_imf_init(); // create a new window, with size=WIDTHxHEIGHT and default engine ee = ecore_evas_new(NULL, 0, 0, WIDTH, HEIGHT, NULL); if (!ee) { fprintf(stderr, "failed to call ecore_evas_new\n"); return EXIT_FAILURE; } ecore_evas_show(ee); // get the canvas off just-created window evas = ecore_evas_get(ee); if (!evas) { fprintf(stderr, "failed to call ecore_evas_get\n"); return EXIT_FAILURE; } // create input field rectangle Evas_Object *bg = evas_object_rectangle_add(evas); evas_object_move(bg, 0, 0); evas_object_resize(bg, WIDTH, HEIGHT); evas_object_color_set(bg, 255, 255, 255, 255); evas_object_show(bg); // register canvas focus in/out event handler evas_event_callback_add(evas, EVAS_CALLBACK_CANVAS_FOCUS_IN, _canvas_focus_in_cb, NULL); evas_event_callback_add(evas, EVAS_CALLBACK_CANVAS_FOCUS_OUT, _canvas_focus_out_cb, NULL); memset(&en1, 0, sizeof(en1)); memset(&en2, 0, sizeof(en2)); // create input field 1 create_input_field(evas, &en1, 40, 60, 400, 80); // create input field 2 create_input_field(evas, &en2, 40, 180, 400, 80); // give focus to input field 1 evas_object_focus_set(en1.rect, EINA_TRUE); ecore_main_loop_begin(); // begin mainloop delete_input_field(&en1); // delete input field 1 delete_input_field(&en2); // delete input field 2 evas_event_callback_del_full(evas, EVAS_CALLBACK_CANVAS_FOCUS_IN, _canvas_focus_in_cb, NULL); evas_event_callback_del_full(evas, EVAS_CALLBACK_CANVAS_FOCUS_OUT, _canvas_focus_out_cb, NULL); ecore_evas_free(ee); ecore_imf_shutdown(); ecore_evas_shutdown(); return 0; }