add ecore_imf_example code. I will add Makefile.am in example directory later
authorjihoon <jihoon@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Fri, 1 Jun 2012 12:46:24 +0000 (12:46 +0000)
committerjihoon <jihoon@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Fri, 1 Jun 2012 12:46:24 +0000 (12:46 +0000)
git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/ecore@71620 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

doc/examples.dox
src/examples/ecore_imf_example.c [new file with mode: 0644]

index d7867a9..cc7ec9d 100644 (file)
@@ -22,6 +22,7 @@
  * @li @ref Ecore_Evas_Buffer_Example_01_c
  * @li @ref Ecore_Evas_Buffer_Example_02_c
  * @li @ref Ecore_exe_simple_example_c
+ * @li @ref ecore_imf_example_c
  */
 
 /**
  * communicate with it.
  *
  */
+
+/**
+ * @page ecore_imf_example_c ecore_imf - How to handle preedit and commit string from Input Method Framework
+ *
+ * This example demonstrates how to connect input method framework and handle preedit and commit string from input method framework.
+ *
+ * To input Chinese, Japanese, Korean and other complex languages, the editor should be connected with input method framework.
+ * 
+ * How to initialize and shutdown ecore imf module
+ * @li ecore_imf_init() should be called to initialize and load immodule.
+ * @li ecore_imf_shutdown() is used for shutdowning and unloading immodule.
+ *
+ * How to create input context and register pre-edit and commit event handler
+ *
+ * Each entry should have each input context to connect with input service framework. 
+ * Key event is processed by input method engine. 
+ * The result is notified to application through ECORE_IMF_CALLBACK_PREEDIT_CHANGED and ECORE_IMF_CALLBACK_COMMIT event.
+ *
+ * The full example follows. 
+ *
+ * @include ecore_imf_example.c
+ */
diff --git a/src/examples/ecore_imf_example.c b/src/examples/ecore_imf_example.c
new file mode 100644 (file)
index 0000000..e3060f5
--- /dev/null
@@ -0,0 +1,605 @@
+/**
+ * Ecore example illustrating how to use ecore imf.
+ *
+ * @verbatim
+ * gcc -o ecore_imf_example ecore_imf_example.c `pkg-config --cflags --libs ecore evas ecore-evas ecore-imf ecore-imf-evas`
+ * @endverbatim
+ */
+
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+#include <Ecore_IMF.h>
+#include <Ecore_IMF_Evas.h>
+#include <Evas.h>
+#include <stdio.h>
+
+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 _mouse_up_cb(void *data, Evas *e, Evas_Object *o, void *event_info)
+{
+    Entry *en = data;
+    if (!en) return;
+
+    evas_object_focus_set(en->rect, EINA_TRUE);
+}
+
+static void _entry_focus_in_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+    Entry *en = data;
+    if (!en) return;
+
+    if (en->imf_context)
+        ecore_imf_context_focus_in(en->imf_context);
+}
+
+static void _entry_focus_out_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
+{
+    Entry *en = data;
+    if (!en) return;
+
+    if (en->imf_context)
+    {
+        ecore_imf_context_reset(en->imf_context);
+        ecore_imf_context_focus_out(en->imf_context);
+    }
+}
+
+static void
+_canvas_focus_in_cb(void *data, Evas *e, void *event_info)
+{
+    Evas_Object *obj = evas_focus_get(e);
+    if (obj)
+        _entry_focus_in_cb(obj, NULL, NULL, NULL);
+}
+
+static void
+_canvas_focus_out_cb(void *data, Evas *e, void *event_info)
+{
+    Evas_Object *obj = evas_focus_get(e);
+    if (obj)
+        _entry_focus_out_cb(obj, 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)
+
+    if (!en) return;
+
+     // Get cursor geometry
+    evas_object_geometry_get(en->txt_obj, &x, &y, &w, &h);
+    evas_textblock_cursor_geometry_get(en->cursor, &cx, &cy, &cw, &ch, NULL, 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);
+}
+
+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, 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;
+
+    str = evas_object_textblock_text_markup_get(en->txt_obj);
+    *text = str ? strdup(str) : strdup("");
+
+    /* Get the current position of cursor */
+    if (cursor_pos)
+        *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, 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) return;
+
+    cursor_pos = evas_textblock_cursor_pos_get(en->cursor);
+
+    del_start = evas_object_textblock_cursor_new(en->txt_obj);
+    evas_textblock_cursor_pos_set(del_start, cursor_pos + ev->offset);
+
+    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, 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);
+
+    evas_object_textblock_text_markup_prepend(en->cursor, commit_str);
+
+    /* Notify 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)
+{
+    /* 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) return;
+
+    /* Get preedit string, 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 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, Evas_Object *obj, void *event_info)
+{
+    Entry *en = data;
+    Evas_Event_Key_Down *ev = event_info;
+    Eina_Bool control, alt, shift;
+    Eina_Bool multiline;
+    Eina_Bool cursor_changed;
+    if (!en) return;
+    if (!ev->key) 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");
+
+    if ((!strcmp(ev->keyname, "Escape")) ||
+            (!strcmp(ev->keyname, "Return")) || (!strcmp(ev->keyname, "KP_Enter")))
+        ecore_imf_context_reset(en->imf_context);
+
+    if (!strcmp(ev->key, "Escape"))
+    {
+        // 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"))
+    {
+        // FILLME
+    }
+    else if (!strcmp(ev->key, "Down") || !strcmp(ev->key, "KP_Down"))
+    {
+        // FILLME
+    }
+    else if (!strcmp(ev->key, "Left") || !strcmp(ev->key, "KP_Left"))
+    {
+        // FILLME
+    }
+    else if (!strcmp(ev->key, "Right") || !strcmp(ev->key, "KP_Right"))
+    {
+        // FILLME
+    }
+    else if (!strcmp(ev->key, "BackSpace"))
+    {
+        if (evas_textblock_cursor_char_prev(en->cursor))
+            evas_textblock_cursor_char_delete(en->cursor);
+
+        return;
+    }
+    else if (!strcmp(ev->key, "Delete") || !strcmp(ev->key, "KP_Delete"))
+    {
+        // FILLME
+    }
+    else if (!strcmp(ev->key, "Home") || !strcmp(ev->key, "KP_Home"))
+    {
+        // FILLME
+    }
+    else if (!strcmp(ev->key, "End") || !strcmp(ev->key, "KP_End"))
+    {
+        // 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
+    }
+    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, "Tab"))
+    {
+        // FILLME
+    }
+    else if ((!strcmp(ev->key, "ISO_Left_Tab")) && (multiline))
+    {
+        // remove a tab
+    }
+    else if (!strcmp(ev->key, "Prior") || !strcmp(ev->key, "KP_Prior"))
+    {
+        // FILLME
+    }
+    else if (!strcmp(ev->key, "Next") || !strcmp(ev->key, "KP_Next"))
+    {
+        // 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 cursor information */
+    _imf_cursor_info_set(en);
+}
+
+static void
+_key_up_cb(void *data, Evas *e, Evas_Object *obj, 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;
+
+    /* Create background for text input field */
+    en->rect = evas_object_rectangle_add(evas);
+    evas_object_size_hint_weight_set(en->rect, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+    evas_object_color_set(en->rect, 150, 150, 150, 255); /* gray */
+    evas_object_move(en->rect, x, y);
+    evas_object_resize(en->rect, w, h);
+    evas_object_show(en->rect);
+
+    /* 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* defaultContextID = ecore_imf_context_default_id_get();
+    if (!defaultContextID)
+        return;
+
+    en->imf_context = ecore_imf_context_add(defaultContextID);
+    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);
+
+    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);
+    evas_object_event_callback_add(en->rect, EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, en);
+    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);
+
+    en->have_preedit = EINA_FALSE;
+    en->preedit_start = NULL;
+    en->preedit_end = NULL;
+
+    /* register retrieve surrounding callback */
+    /* This is used for autocapital or autoperiod */
+    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 */
+    /* This is used for autocapital or autoperiod */
+    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(int argc, char *argv[])
+{
+    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=480x800 and default engine
+    ee = ecore_evas_new(NULL, 0, 0, 480, 800, NULL);
+
+    if (!ee) {
+        fprintf(stderr, "something went wrong... :(\n");
+        return EXIT_FAILURE;
+    }
+
+    ecore_evas_show(ee);
+
+    // Get the canvas off just-created window
+    evas = ecore_evas_get(ee);
+    if (!evas) {
+        fprintf(stderr, "something went wrong... :(\n");
+        return EXIT_FAILURE;
+    }
+
+    // Create input field rectangle
+    Evas_Object* bg = evas_object_rectangle_add(evas);
+    evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+    evas_object_move(bg, 0, 0);
+    evas_object_resize(bg, 480, 800);
+    evas_object_color_set(bg, 255, 255, 255, 255);
+    evas_object_show(bg);
+
+    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);
+
+    // 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;
+}